mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
feat: systemd management (#532)
This commit is contained in:
@@ -690,7 +690,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -700,7 +700,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -826,7 +826,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -836,7 +836,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -854,7 +854,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -864,7 +864,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -885,7 +885,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -898,7 +898,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||||
@@ -924,7 +924,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -937,7 +937,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -960,7 +960,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -973,7 +973,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -996,7 +996,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1008,7 +1008,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||||
@@ -1037,7 +1037,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1049,7 +1049,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||||
PRODUCT_NAME = ServerBox;
|
PRODUCT_NAME = ServerBox;
|
||||||
@@ -1075,7 +1075,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1087,7 +1087,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||||
PRODUCT_NAME = ServerBox;
|
PRODUCT_NAME = ServerBox;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ typedef _OnStdin = void Function(SSHSession session);
|
|||||||
typedef PwdRequestFunc = Future<String?> Function(String? user);
|
typedef PwdRequestFunc = Future<String?> Function(String? user);
|
||||||
|
|
||||||
extension SSHClientX on SSHClient {
|
extension SSHClientX on SSHClient {
|
||||||
|
/// TODO: delete [exec]
|
||||||
Future<SSHSession> exec(
|
Future<SSHSession> exec(
|
||||||
String cmd, {
|
String cmd, {
|
||||||
_OnStdout? onStderr,
|
_OnStdout? onStderr,
|
||||||
@@ -135,4 +136,36 @@ extension SSHClientX on SSHClient {
|
|||||||
|
|
||||||
return result.takeBytes();
|
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);
|
||||||
|
session.stdin.close();
|
||||||
|
|
||||||
|
await stdoutDone.future;
|
||||||
|
await stderrDone.future;
|
||||||
|
|
||||||
|
return result.takeBytes().string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/provider/app.dart';
|
||||||
|
|
||||||
abstract final class KeybordInteractive {
|
abstract final class KeybordInteractive {
|
||||||
static FutureOr<List<String>?> defaultHandle(
|
static FutureOr<List<String>?> defaultHandle(
|
||||||
@@ -12,8 +12,8 @@ abstract final class KeybordInteractive {
|
|||||||
BuildContext? ctx,
|
BuildContext? ctx,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final res = await (ctx ?? Pros.app.ctx)?.showPwdDialog(
|
final res = await (ctx ?? AppProvider.ctx)?.showPwdDialog(
|
||||||
title: '2FA ${l10n.pwd}',
|
title: l10n.pwd,
|
||||||
id: spi.id,
|
id: spi.id,
|
||||||
label: spi.id,
|
label: spi.id,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,29 +2,47 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
part 'server_func.g.dart';
|
part 'server_func.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: 6)
|
@HiveType(typeId: 6)
|
||||||
enum ServerFuncBtn {
|
enum ServerFuncBtn {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
terminal,
|
terminal._(),
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
sftp,
|
sftp._(),
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
container,
|
container._(),
|
||||||
@HiveField(3)
|
@HiveField(3)
|
||||||
process,
|
process._(),
|
||||||
//@HiveField(4)
|
//@HiveField(4)
|
||||||
//pkg,
|
//pkg,
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
snippet,
|
snippet._(),
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
iperf,
|
iperf._(),
|
||||||
// @HiveField(7)
|
// @HiveField(7)
|
||||||
// pve,
|
// pve,
|
||||||
|
@HiveField(8)
|
||||||
|
systemd._(1058),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
final int? addedVersion;
|
||||||
|
|
||||||
|
const ServerFuncBtn._([this.addedVersion]);
|
||||||
|
|
||||||
|
static void autoAddNewFuncs(int cur) {
|
||||||
|
if (cur >= systemd.addedVersion!) {
|
||||||
|
final prop = Stores.setting.serverFuncBtns;
|
||||||
|
final list = prop.fetch();
|
||||||
|
if (!list.contains(systemd.index)) {
|
||||||
|
list.add(systemd.index);
|
||||||
|
prop.put(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static final defaultIdxs = [
|
static final defaultIdxs = [
|
||||||
terminal,
|
terminal,
|
||||||
sftp,
|
sftp,
|
||||||
@@ -32,6 +50,7 @@ enum ServerFuncBtn {
|
|||||||
process,
|
process,
|
||||||
//pkg,
|
//pkg,
|
||||||
snippet,
|
snippet,
|
||||||
|
systemd,
|
||||||
].map((e) => e.index).toList();
|
].map((e) => e.index).toList();
|
||||||
|
|
||||||
IconData get icon => switch (this) {
|
IconData get icon => switch (this) {
|
||||||
@@ -42,6 +61,7 @@ enum ServerFuncBtn {
|
|||||||
process => Icons.list_alt_outlined,
|
process => Icons.list_alt_outlined,
|
||||||
terminal => Icons.terminal,
|
terminal => Icons.terminal,
|
||||||
iperf => Icons.speed,
|
iperf => Icons.speed,
|
||||||
|
systemd => MingCute.plugin_2_fill,
|
||||||
};
|
};
|
||||||
|
|
||||||
String get toStr => switch (this) {
|
String get toStr => switch (this) {
|
||||||
@@ -52,5 +72,6 @@ enum ServerFuncBtn {
|
|||||||
process => l10n.process,
|
process => l10n.process,
|
||||||
terminal => l10n.terminal,
|
terminal => l10n.terminal,
|
||||||
iperf => 'iperf',
|
iperf => 'iperf',
|
||||||
|
systemd => 'Systemd',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class ServerFuncBtnAdapter extends TypeAdapter<ServerFuncBtn> {
|
|||||||
return ServerFuncBtn.snippet;
|
return ServerFuncBtn.snippet;
|
||||||
case 6:
|
case 6:
|
||||||
return ServerFuncBtn.iperf;
|
return ServerFuncBtn.iperf;
|
||||||
|
case 8:
|
||||||
|
return ServerFuncBtn.systemd;
|
||||||
default:
|
default:
|
||||||
return ServerFuncBtn.terminal;
|
return ServerFuncBtn.terminal;
|
||||||
}
|
}
|
||||||
@@ -51,6 +53,9 @@ class ServerFuncBtnAdapter extends TypeAdapter<ServerFuncBtn> {
|
|||||||
case ServerFuncBtn.iperf:
|
case ServerFuncBtn.iperf:
|
||||||
writer.writeByte(6);
|
writer.writeByte(6);
|
||||||
break;
|
break;
|
||||||
|
case ServerFuncBtn.systemd:
|
||||||
|
writer.writeByte(8);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
118
lib/data/model/server/systemd.dart
Normal file
118
lib/data/model/server/systemd.dart
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum SystemdUnitFunc {
|
||||||
|
start,
|
||||||
|
stop,
|
||||||
|
restart,
|
||||||
|
reload,
|
||||||
|
enable,
|
||||||
|
disable,
|
||||||
|
status,
|
||||||
|
;
|
||||||
|
|
||||||
|
IconData get icon => switch (this) {
|
||||||
|
start => Icons.play_arrow,
|
||||||
|
stop => Icons.stop,
|
||||||
|
restart => Icons.refresh,
|
||||||
|
reload => Icons.refresh,
|
||||||
|
enable => Icons.check,
|
||||||
|
disable => Icons.close,
|
||||||
|
status => Icons.info,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SystemdUnitType {
|
||||||
|
service,
|
||||||
|
socket,
|
||||||
|
mount,
|
||||||
|
timer,
|
||||||
|
;
|
||||||
|
|
||||||
|
static SystemdUnitType? fromString(String? value) {
|
||||||
|
return values.firstWhereOrNull((e) => e.name == value?.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SystemdUnitScope {
|
||||||
|
system,
|
||||||
|
user,
|
||||||
|
;
|
||||||
|
|
||||||
|
Color? get color => switch (this) {
|
||||||
|
system => Colors.red,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
String getCmdPrefix(bool isRoot) {
|
||||||
|
if (this == system) {
|
||||||
|
return isRoot ? 'systemctl' : 'sudo systemctl';
|
||||||
|
}
|
||||||
|
return 'systemctl --user';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SystemdUnitState {
|
||||||
|
active,
|
||||||
|
inactive,
|
||||||
|
failed,
|
||||||
|
activating,
|
||||||
|
deactivating,
|
||||||
|
;
|
||||||
|
|
||||||
|
static SystemdUnitState? fromString(String? value) {
|
||||||
|
return values.firstWhereOrNull((e) => e.name == value?.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
Color? get color => switch (this) {
|
||||||
|
failed => Colors.red,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
final class SystemdUnit {
|
||||||
|
final String name;
|
||||||
|
final String? description;
|
||||||
|
final SystemdUnitType type;
|
||||||
|
final SystemdUnitScope scope;
|
||||||
|
final SystemdUnitState state;
|
||||||
|
|
||||||
|
SystemdUnit({
|
||||||
|
required this.name,
|
||||||
|
this.description,
|
||||||
|
required this.type,
|
||||||
|
required this.scope,
|
||||||
|
required this.state,
|
||||||
|
});
|
||||||
|
|
||||||
|
String getCmd({
|
||||||
|
required SystemdUnitFunc func,
|
||||||
|
required bool isRoot,
|
||||||
|
}) {
|
||||||
|
final prefix = scope.getCmdPrefix(isRoot);
|
||||||
|
return '$prefix ${func.name} $name';
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SystemdUnitFunc> get availableFuncs {
|
||||||
|
final funcs = <SystemdUnitFunc>{};
|
||||||
|
switch (state) {
|
||||||
|
case SystemdUnitState.active:
|
||||||
|
funcs.addAll([SystemdUnitFunc.stop, SystemdUnitFunc.restart]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.inactive:
|
||||||
|
funcs.addAll([SystemdUnitFunc.start]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.failed:
|
||||||
|
funcs.addAll([SystemdUnitFunc.restart]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.activating:
|
||||||
|
funcs.addAll([SystemdUnitFunc.stop]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.deactivating:
|
||||||
|
funcs.addAll([SystemdUnitFunc.start]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
funcs.addAll([SystemdUnitFunc.status]);
|
||||||
|
return funcs.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,7 @@
|
|||||||
import 'package:device_info_plus/device_info_plus.dart';
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AppProvider extends ChangeNotifier {
|
final class AppProvider {
|
||||||
BuildContext? ctx;
|
const AppProvider._();
|
||||||
|
|
||||||
bool isWearOS = false;
|
static BuildContext? ctx;
|
||||||
|
|
||||||
Future<void> init() async {
|
|
||||||
await _initIsWearOS();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _initIsWearOS() async {
|
|
||||||
if (!isAndroid) {
|
|
||||||
isWearOS = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final deviceInfo = DeviceInfoPlugin();
|
|
||||||
final androidInfo = await deviceInfo.androidInfo;
|
|
||||||
|
|
||||||
const feat = 'android.hardware.type.watch';
|
|
||||||
final hasFeat = androidInfo.systemFeatures.contains(feat);
|
|
||||||
if (hasFeat) {
|
|
||||||
isWearOS = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
167
lib/data/provider/systemd.dart
Normal file
167
lib/data/provider/systemd.dart
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:server_box/core/extension/ssh_client.dart';
|
||||||
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:server_box/data/model/server/systemd.dart';
|
||||||
|
import 'package:server_box/data/res/provider.dart';
|
||||||
|
|
||||||
|
final class SystemdProvider {
|
||||||
|
late final SSHClient _client;
|
||||||
|
|
||||||
|
SystemdProvider.init(ServerPrivateInfo spi) {
|
||||||
|
_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 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)) {
|
||||||
|
if (unit.startsWith('/etc/systemd/system')) {
|
||||||
|
systemUnits.add(unit);
|
||||||
|
} else if (unit.startsWith('~/.config/systemd/user')) {
|
||||||
|
userUnits.add(unit);
|
||||||
|
} else if (unit.trim().isNotEmpty) {
|
||||||
|
Loggers.app.warning('Unknown unit: $unit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final parsedUserUnits =
|
||||||
|
await _parseUnitObj(userUnits, SystemdUnitScope.user);
|
||||||
|
final parsedSystemUnits =
|
||||||
|
await _parseUnitObj(systemUnits, SystemdUnitScope.system);
|
||||||
|
this.units.value = [...parsedUserUnits, ...parsedSystemUnits];
|
||||||
|
} catch (e, s) {
|
||||||
|
Loggers.app.warning('Parse systemd', e, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
isBusy.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<SystemdUnit>> _parseUnitObj(
|
||||||
|
List<String> unitNames,
|
||||||
|
SystemdUnitScope scope,
|
||||||
|
) async {
|
||||||
|
final unitNames_ = unitNames
|
||||||
|
.map((e) => e.trim().split('/').last.split('.').first)
|
||||||
|
.toList();
|
||||||
|
final script = '''
|
||||||
|
for unit in ${unitNames_.join(' ')}; do
|
||||||
|
state=\$(systemctl show --no-pager \$unit)
|
||||||
|
echo -n "${ShellFunc.seperator}\n\$state"
|
||||||
|
done
|
||||||
|
''';
|
||||||
|
final result = await _client.runScriptIn(script);
|
||||||
|
final units = result.split(ShellFunc.seperator);
|
||||||
|
|
||||||
|
final parsedUnits = <SystemdUnit>[];
|
||||||
|
for (final unit in units) {
|
||||||
|
final parts = unit.split('\n');
|
||||||
|
var name = '';
|
||||||
|
var type = '';
|
||||||
|
var state = '';
|
||||||
|
String? description;
|
||||||
|
for (final part in parts) {
|
||||||
|
if (part.startsWith('Id=')) {
|
||||||
|
final val = _getIniVal(part).split('.');
|
||||||
|
name = val.first;
|
||||||
|
type = val.last;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (part.startsWith('ActiveState=')) {
|
||||||
|
state = _getIniVal(part);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (part.startsWith('Description=')) {
|
||||||
|
description = _getIniVal(part);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final unitType = SystemdUnitType.fromString(type);
|
||||||
|
if (unitType == null) {
|
||||||
|
Loggers.app.warning('Unit type: $type');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final unitState = SystemdUnitState.fromString(state);
|
||||||
|
if (unitState == null) {
|
||||||
|
Loggers.app.warning('Unit state: $state');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedUnits.add(SystemdUnit(
|
||||||
|
name: name,
|
||||||
|
type: unitType,
|
||||||
|
scope: scope,
|
||||||
|
state: unitState,
|
||||||
|
description: description,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedUnits.sort((a, b) {
|
||||||
|
// user units first
|
||||||
|
if (a.scope != b.scope) {
|
||||||
|
return a.scope == SystemdUnitScope.user ? -1 : 1;
|
||||||
|
}
|
||||||
|
// active units first
|
||||||
|
if (a.state != b.state) {
|
||||||
|
return a.state == SystemdUnitState.active ? -1 : 1;
|
||||||
|
}
|
||||||
|
return a.name.compareTo(b.name);
|
||||||
|
});
|
||||||
|
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
|
||||||
|
|
||||||
|
get_files() {
|
||||||
|
unit_type=\$1
|
||||||
|
base_dir=\$2
|
||||||
|
|
||||||
|
# If base_dir is not a directory, return
|
||||||
|
if [ ! -d "\$base_dir" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
find "\$base_dir" -type f -name "*.\$unit_type" -print | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
types="service socket mount timer"
|
||||||
|
|
||||||
|
for type in \$types; do
|
||||||
|
get_type_files \$type
|
||||||
|
done
|
||||||
|
''';
|
||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 1057;
|
static const int build = 1058;
|
||||||
static const int script = 56;
|
static const int script = 56;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import 'package:server_box/data/provider/app.dart';
|
|
||||||
import 'package:server_box/data/provider/private_key.dart';
|
import 'package:server_box/data/provider/private_key.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
import 'package:server_box/data/provider/sftp.dart';
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
import 'package:server_box/data/provider/snippet.dart';
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
|
|
||||||
abstract final class Pros {
|
abstract final class Pros {
|
||||||
static final app = AppProvider();
|
|
||||||
static final key = PrivateKeyProvider();
|
static final key = PrivateKeyProvider();
|
||||||
static final server = ServerProvider();
|
static final server = ServerProvider();
|
||||||
static final sftp = SftpProvider();
|
static final sftp = SftpProvider();
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ Future<void> main() async {
|
|||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (_) => Pros.app),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.server),
|
ChangeNotifierProvider(create: (_) => Pros.server),
|
||||||
ChangeNotifierProvider(create: (_) => Pros.snippet),
|
ChangeNotifierProvider(create: (_) => Pros.snippet),
|
||||||
ChangeNotifierProvider(create: (_) => Pros.key),
|
ChangeNotifierProvider(create: (_) => Pros.key),
|
||||||
@@ -97,7 +96,6 @@ Future<void> _initData() async {
|
|||||||
|
|
||||||
Pros.snippet.load();
|
Pros.snippet.load();
|
||||||
Pros.key.load();
|
Pros.key.load();
|
||||||
await Pros.app.init();
|
|
||||||
|
|
||||||
if (Stores.setting.betaTest.fetch()) AppUpdate.chan = AppUpdateChan.beta;
|
if (Stores.setting.betaTest.fetch()) AppUpdate.chan = AppUpdateChan.beta;
|
||||||
}
|
}
|
||||||
@@ -136,6 +134,7 @@ Future<void> _doVersionRelated() async {
|
|||||||
// How to upgrade the data is inside each own func.
|
// How to upgrade the data is inside each own func.
|
||||||
if (curVer < newVer) {
|
if (curVer < newVer) {
|
||||||
ServerDetailCards.autoAddNewCards(newVer);
|
ServerDetailCards.autoAddNewCards(newVer);
|
||||||
|
ServerFuncBtn.autoAddNewFuncs(newVer);
|
||||||
Stores.setting.lastVer.put(newVer);
|
Stores.setting.lastVer.put(newVer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:server_box/core/extension/build.dart';
|
|||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/core/route.dart';
|
import 'package:server_box/core/route.dart';
|
||||||
import 'package:server_box/data/model/app/tab.dart';
|
import 'package:server_box/data/model/app/tab.dart';
|
||||||
|
import 'package:server_box/data/provider/app.dart';
|
||||||
import 'package:server_box/data/res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:server_box/data/res/github_id.dart';
|
import 'package:server_box/data/res/github_id.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
@@ -103,7 +104,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
Pros.app.ctx = context;
|
AppProvider.ctx = context;
|
||||||
|
|
||||||
final appBar = _AppBar(
|
final appBar = _AppBar(
|
||||||
selectIndex: _selectIndex,
|
selectIndex: _selectIndex,
|
||||||
|
|||||||
165
lib/view/page/systemd.dart
Normal file
165
lib/view/page/systemd.dart
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:server_box/core/route.dart';
|
||||||
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:server_box/data/model/server/systemd.dart';
|
||||||
|
import 'package:server_box/data/provider/systemd.dart';
|
||||||
|
|
||||||
|
final class SystemdPageArgs {
|
||||||
|
final ServerPrivateInfo spi;
|
||||||
|
|
||||||
|
const SystemdPageArgs({
|
||||||
|
required this.spi,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final class SystemdPage extends StatefulWidget {
|
||||||
|
final SystemdPageArgs args;
|
||||||
|
|
||||||
|
const SystemdPage({
|
||||||
|
super.key,
|
||||||
|
required this.args,
|
||||||
|
});
|
||||||
|
|
||||||
|
static const route = AppRoute<void, SystemdPageArgs>(
|
||||||
|
page: SystemdPage.new,
|
||||||
|
path: '/systemd',
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SystemdPage> createState() => _SystemdPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _SystemdPageState extends State<SystemdPage> {
|
||||||
|
late final _pro = SystemdProvider.init(widget.args.spi);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: CustomAppBar(
|
||||||
|
title: const Text('Systemd'),
|
||||||
|
actions: [
|
||||||
|
Btn.icon(icon: const Icon(Icons.refresh), onTap: _pro.getUnits),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: _buildBody(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody() {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: _pro.isBusy.listenVal(
|
||||||
|
(isBusy) => AnimatedContainer(
|
||||||
|
duration: Durations.medium1,
|
||||||
|
curve: Curves.fastEaseInToSlowEaseOut,
|
||||||
|
height: isBusy ? 50 : 0,
|
||||||
|
child: isBusy
|
||||||
|
? UIs.centerSizedLoadingSmall.paddingOnly(bottom: 7)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildUnitList(_pro.units),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildUnitList(VNode<List<SystemdUnit>> units) {
|
||||||
|
return units.listenVal(
|
||||||
|
(units) {
|
||||||
|
if (units.isEmpty) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: ListTile(title: Text(libL10n.empty))
|
||||||
|
.cardx
|
||||||
|
.paddingSymmetric(horizontal: 13),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
final unit = units[index];
|
||||||
|
return ListTile(
|
||||||
|
leading: _buildScopeTag(unit.scope),
|
||||||
|
title: unit.description != null
|
||||||
|
? TipText(unit.name, unit.description!)
|
||||||
|
: Text(unit.name),
|
||||||
|
subtitle: Wrap(children: [
|
||||||
|
_buildStateTag(unit.state),
|
||||||
|
_buildTypeTag(unit.type),
|
||||||
|
]).paddingOnly(top: 7),
|
||||||
|
trailing: _buildUnitFuncs(unit),
|
||||||
|
).cardx.paddingSymmetric(horizontal: 13);
|
||||||
|
},
|
||||||
|
childCount: units.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildUnitFuncs(SystemdUnit unit) {
|
||||||
|
return PopupMenu(
|
||||||
|
items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(),
|
||||||
|
onSelected: (val) async {
|
||||||
|
final cmd = unit.getCmd(func: val, isRoot: _pro.isRoot.value);
|
||||||
|
final sure = await context.showRoundDialog(
|
||||||
|
title: libL10n.attention,
|
||||||
|
child: SimpleMarkdown(data: '```shell\n$cmd\n```'),
|
||||||
|
actions: [
|
||||||
|
CountDownBtn(
|
||||||
|
seconds: 1,
|
||||||
|
onTap: () => context.pop(true),
|
||||||
|
text: libL10n.ok,
|
||||||
|
afterColor: Colors.red,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
if (sure != true) return;
|
||||||
|
|
||||||
|
AppRoutes.ssh(spi: widget.args.spi, initCmd: cmd).go(context);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupMenuEntry _buildUnitFuncBtn(SystemdUnitFunc func) {
|
||||||
|
return PopupMenuItem<SystemdUnitFunc>(
|
||||||
|
value: func,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Icon(func.icon, size: 19),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(func.name.upperFirst),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildScopeTag(SystemdUnitScope scope) {
|
||||||
|
return _buildTag(scope.name.upperFirst, scope.color, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStateTag(SystemdUnitState state) {
|
||||||
|
return _buildTag(state.name.upperFirst, state.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTypeTag(SystemdUnitType type) {
|
||||||
|
return _buildTag(type.name.upperFirst);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTag(String tag, [Color? color, bool noPad = false]) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color?.withOpacity(0.7) ?? UIs.halfAlpha,
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
tag,
|
||||||
|
style: UIs.text11Grey,
|
||||||
|
).paddingSymmetric(horizontal: 5, vertical: 1),
|
||||||
|
).paddingOnly(right: noPad ? 0 : 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import 'package:server_box/data/model/app/menu/server_func.dart';
|
|||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/res/provider.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
import 'package:server_box/view/page/systemd.dart';
|
||||||
|
|
||||||
import '../../core/route.dart';
|
import '../../core/route.dart';
|
||||||
import '../../core/utils/server.dart';
|
import '../../core/utils/server.dart';
|
||||||
@@ -162,6 +163,12 @@ void _onTapMoreBtns(
|
|||||||
check: () => _checkClient(context, spi.id),
|
check: () => _checkClient(context, spi.id),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case ServerFuncBtn.systemd:
|
||||||
|
SystemdPage.route.go(
|
||||||
|
context,
|
||||||
|
args: SystemdPageArgs(spi: spi),
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import device_info_plus
|
|
||||||
import dynamic_color
|
import dynamic_color
|
||||||
import icloud_storage
|
import icloud_storage
|
||||||
import local_auth_darwin
|
import local_auth_darwin
|
||||||
@@ -20,7 +19,6 @@ import wakelock_plus
|
|||||||
import window_manager
|
import window_manager
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
|
||||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||||
IcloudStoragePlugin.register(with: registry.registrar(forPlugin: "IcloudStoragePlugin"))
|
IcloudStoragePlugin.register(with: registry.registrar(forPlugin: "IcloudStoragePlugin"))
|
||||||
FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin"))
|
FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin"))
|
||||||
|
|||||||
@@ -471,7 +471,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||||
@@ -481,7 +481,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "Server Box";
|
PRODUCT_NAME = "Server Box";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -608,7 +608,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||||
@@ -618,7 +618,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "Server Box";
|
PRODUCT_NAME = "Server Box";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -638,7 +638,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1057;
|
CURRENT_PROJECT_VERSION = 1058;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -649,7 +649,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
MARKETING_VERSION = 1.0.1057;
|
MARKETING_VERSION = 1.0.1058;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "Server Box";
|
PRODUCT_NAME = "Server Box";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
24
pubspec.lock
24
pubspec.lock
@@ -273,22 +273,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.10"
|
version: "0.7.10"
|
||||||
device_info_plus:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: device_info_plus
|
|
||||||
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "10.1.2"
|
|
||||||
device_info_plus_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: device_info_plus_platform_interface
|
|
||||||
sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "7.0.1"
|
|
||||||
dio:
|
dio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1559,14 +1543,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.5.3"
|
version: "5.5.3"
|
||||||
win32_registry:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: win32_registry
|
|
||||||
sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.4"
|
|
||||||
window_manager:
|
window_manager:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: server_box
|
name: server_box
|
||||||
description: server status & toolbox app.
|
description: server status & toolbox app.
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.0.1057+1057
|
version: 1.0.1058+1058
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0"
|
sdk: ">=3.0.0"
|
||||||
@@ -28,7 +28,6 @@ dependencies:
|
|||||||
wakelock_plus: ^1.2.4
|
wakelock_plus: ^1.2.4
|
||||||
wake_on_lan: ^4.1.1+3
|
wake_on_lan: ^4.1.1+3
|
||||||
flutter_adaptive_scaffold: ^0.1.10+2
|
flutter_adaptive_scaffold: ^0.1.10+2
|
||||||
device_info_plus: ^10.1.0
|
|
||||||
extended_image: ^8.2.1
|
extended_image: ^8.2.1
|
||||||
dartssh2:
|
dartssh2:
|
||||||
git:
|
git:
|
||||||
|
|||||||
Reference in New Issue
Block a user