mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
feat & fix
- support openwrt - save server info failed if connect error - support ssh 'none' auth
This commit is contained in:
BIN
assets/linux/wrt.png
Normal file
BIN
assets/linux/wrt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -2,14 +2,12 @@ class Memory {
|
||||
int total;
|
||||
int used;
|
||||
int free;
|
||||
int shared;
|
||||
int cache;
|
||||
int avail;
|
||||
Memory(
|
||||
{required this.total,
|
||||
required this.used,
|
||||
required this.free,
|
||||
required this.shared,
|
||||
required this.cache,
|
||||
required this.avail});
|
||||
}
|
||||
|
||||
@@ -37,13 +37,14 @@ const shellCmd = "export LANG=en_US.utf-8 \necho '$seperator' \n"
|
||||
"uptime \necho $seperator \n"
|
||||
"cat /proc/net/snmp \necho $seperator \n"
|
||||
"df -h \necho $seperator \n"
|
||||
"free -m \necho $seperator \n"
|
||||
"cat /proc/meminfo \necho $seperator \n"
|
||||
"cat /sys/class/thermal/thermal_zone*/type \necho $seperator \n"
|
||||
"cat /sys/class/thermal/thermal_zone*/temp";
|
||||
const shellPath = '.serverbox.sh';
|
||||
const memPrefix = 'Mem:';
|
||||
final cpuTempReg = RegExp('(x86_pkg_temp|cpu_thermal)');
|
||||
final numReg = RegExp(r'\s{1,}');
|
||||
final memItemReg = RegExp(r'([A-Z].+:)\s+([0-9]+) kB');
|
||||
|
||||
class ServerProvider extends BusyProvider {
|
||||
List<ServerInfo> _servers = [];
|
||||
@@ -54,7 +55,7 @@ class ServerProvider extends BusyProvider {
|
||||
final logger = Logger('ServerProvider');
|
||||
|
||||
Memory get emptyMemory =>
|
||||
Memory(total: 1, used: 0, free: 1, shared: 0, cache: 0, avail: 1);
|
||||
Memory(total: 1, used: 0, free: 1, cache: 0, avail: 1);
|
||||
|
||||
NetSpeedPart get emptyNetSpeedPart => NetSpeedPart('', 0, 0, 0);
|
||||
|
||||
@@ -156,8 +157,8 @@ class ServerProvider extends BusyProvider {
|
||||
throw RangeError.index(idx, _servers);
|
||||
}
|
||||
_servers[idx].info = newSpi;
|
||||
_servers[idx].client = await genClient(newSpi);
|
||||
locator<ServerStore>().update(old, newSpi);
|
||||
_servers[idx].client = await genClient(newSpi);
|
||||
notifyListeners();
|
||||
refreshData(spi: newSpi);
|
||||
}
|
||||
@@ -194,8 +195,8 @@ class ServerProvider extends BusyProvider {
|
||||
// if client is null, return
|
||||
if (s.client == null) return;
|
||||
final raw = await s.client!.run("sh $shellPath").string;
|
||||
final lines = raw.split(seperator).map((e) => e.trim()).toList();
|
||||
if (raw.isEmpty || lines.length == 1) {
|
||||
final segments = raw.split(seperator).map((e) => e.trim()).toList();
|
||||
if (raw.isEmpty || segments.length == 1) {
|
||||
s.connectionState = ServerConnectionState.failed;
|
||||
if (s.status.failedInfo == null || s.status.failedInfo!.isEmpty) {
|
||||
s.status.failedInfo = 'No data received';
|
||||
@@ -203,16 +204,16 @@ class ServerProvider extends BusyProvider {
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
lines.removeAt(0);
|
||||
segments.removeAt(0);
|
||||
|
||||
try {
|
||||
_getCPU(spi, lines[2], lines[7], lines[8]);
|
||||
_getMem(spi, lines[6]);
|
||||
_getSysVer(spi, lines[1]);
|
||||
_getUpTime(spi, lines[3]);
|
||||
_getDisk(spi, lines[5]);
|
||||
_getTcp(spi, lines[4]);
|
||||
_getNetSpeed(spi, lines[0]);
|
||||
_getCPU(spi, segments[2], segments[7], segments[8]);
|
||||
_getMem(spi, segments[6]);
|
||||
_getSysVer(spi, segments[1]);
|
||||
_getUpTime(spi, segments[3]);
|
||||
_getDisk(spi, segments[5]);
|
||||
_getTcp(spi, segments[4]);
|
||||
_getNetSpeed(spi, segments[0]);
|
||||
} catch (e) {
|
||||
s.connectionState = ServerConnectionState.failed;
|
||||
s.status.failedInfo = e.toString();
|
||||
@@ -333,21 +334,22 @@ class ServerProvider extends BusyProvider {
|
||||
|
||||
void _getMem(ServerPrivateInfo spi, String raw) {
|
||||
final info = _servers.firstWhere((e) => e.info == spi);
|
||||
for (var item in raw.split('\n')) {
|
||||
if (item.contains(memPrefix)) {
|
||||
final split = item.replaceFirst(memPrefix, '').split(' ');
|
||||
split.removeWhere((e) => e == '');
|
||||
final memList = split.map((e) => int.parse(e)).toList();
|
||||
info.status.memory = Memory(
|
||||
total: memList[0],
|
||||
used: memList[1],
|
||||
free: memList[2],
|
||||
shared: memList[3],
|
||||
cache: memList[4],
|
||||
avail: memList[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
final items = raw.split('\n').map((e) => memItemReg.firstMatch(e)).toList();
|
||||
final total = int.parse(
|
||||
items.firstWhere((e) => e?.group(1) == 'MemTotal:')?.group(2) ?? '1');
|
||||
final free = int.parse(
|
||||
items.firstWhere((e) => e?.group(1) == 'MemFree:')?.group(2) ?? '0');
|
||||
final cached = int.parse(
|
||||
items.firstWhere((e) => e?.group(1) == 'Cached:')?.group(2) ?? '0');
|
||||
final available = int.parse(
|
||||
items.firstWhere((e) => e?.group(1) == 'MemAvailable:')?.group(2) ??
|
||||
'0');
|
||||
info.status.memory = Memory(
|
||||
total: total,
|
||||
used: total - available,
|
||||
free: free,
|
||||
cache: cached,
|
||||
avail: available);
|
||||
}
|
||||
|
||||
Future<String?> runSnippet(ServerPrivateInfo spi, Snippet snippet) async {
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import 'package:toolbox/data/model/server/linux_icon.dart';
|
||||
|
||||
final linuxIcons = LinuxIcons(
|
||||
['ubuntu', 'arch', 'centos', 'debian', 'fedora', 'opensuse', 'kali']);
|
||||
final linuxIcons = LinuxIcons([
|
||||
'ubuntu',
|
||||
'arch',
|
||||
'centos',
|
||||
'debian',
|
||||
'fedora',
|
||||
'opensuse',
|
||||
'kali',
|
||||
'wrt'
|
||||
]);
|
||||
|
||||
@@ -164,8 +164,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Current platform does not support in app update.\nPlease build from source and install it."),
|
||||
"plzEnterHost":
|
||||
MessageLookupByLibrary.simpleMessage("Please enter host."),
|
||||
"plzEnterPwd":
|
||||
MessageLookupByLibrary.simpleMessage("Please enter password."),
|
||||
"plzSelectKey":
|
||||
MessageLookupByLibrary.simpleMessage("Please select a key."),
|
||||
"port": MessageLookupByLibrary.simpleMessage("Port"),
|
||||
@@ -204,6 +202,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
||||
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
|
||||
"sureDelete": m11,
|
||||
"sureNoPwd": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure to use no password?"),
|
||||
"sureToDeleteServer": m12,
|
||||
"ttl": MessageLookupByLibrary.simpleMessage("ttl"),
|
||||
"unknown": MessageLookupByLibrary.simpleMessage("unknown"),
|
||||
|
||||
@@ -145,7 +145,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"platformNotSupportUpdate":
|
||||
MessageLookupByLibrary.simpleMessage("当前平台不支持更新,请编译最新源码后手动安装"),
|
||||
"plzEnterHost": MessageLookupByLibrary.simpleMessage("请输入主机"),
|
||||
"plzEnterPwd": MessageLookupByLibrary.simpleMessage("请输入密码"),
|
||||
"plzSelectKey": MessageLookupByLibrary.simpleMessage("请选择私钥"),
|
||||
"port": MessageLookupByLibrary.simpleMessage("端口"),
|
||||
"privateKey": MessageLookupByLibrary.simpleMessage("私钥"),
|
||||
@@ -178,6 +177,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"start": MessageLookupByLibrary.simpleMessage("开始"),
|
||||
"stop": MessageLookupByLibrary.simpleMessage("停止"),
|
||||
"sureDelete": m11,
|
||||
"sureNoPwd": MessageLookupByLibrary.simpleMessage("确认使用无密码?"),
|
||||
"sureToDeleteServer": m12,
|
||||
"ttl": MessageLookupByLibrary.simpleMessage("缓存时间"),
|
||||
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
||||
|
||||
@@ -790,11 +790,11 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Please enter password.`
|
||||
String get plzEnterPwd {
|
||||
/// `Are you sure to use no password?`
|
||||
String get sureNoPwd {
|
||||
return Intl.message(
|
||||
'Please enter password.',
|
||||
name: 'plzEnterPwd',
|
||||
'Are you sure to use no password?',
|
||||
name: 'sureNoPwd',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"addPrivateKey": "Add private key",
|
||||
"choosePrivateKey": "Choose private key",
|
||||
"plzEnterHost": "Please enter host.",
|
||||
"plzEnterPwd": "Please enter password.",
|
||||
"sureNoPwd": "Are you sure to use no password?",
|
||||
"plzSelectKey": "Please select a key.",
|
||||
"exampleName": "Example name",
|
||||
"stop": "Stop",
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"addPrivateKey": "添加一个私钥",
|
||||
"choosePrivateKey": "选择私钥",
|
||||
"plzEnterHost": "请输入主机",
|
||||
"plzEnterPwd": "请输入密码",
|
||||
"sureNoPwd": "确认使用无密码?",
|
||||
"plzSelectKey": "请选择私钥",
|
||||
"exampleName": "名称示例",
|
||||
"stop": "停止",
|
||||
|
||||
@@ -155,6 +155,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
||||
}
|
||||
|
||||
Widget _buildProgress(double percent) {
|
||||
if (percent > 100) percent = 100;
|
||||
final pColor = primaryColor;
|
||||
final percentWithinOne = percent / 100;
|
||||
return LinearProgressIndicator(
|
||||
@@ -186,7 +187,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
||||
final pColor = primaryColor;
|
||||
final used = ss.memory.used / ss.memory.total;
|
||||
final width = _media.size.width - 17 * 2 - 17 * 2;
|
||||
const mb = 1024 * 1024;
|
||||
const mb = 1024;
|
||||
return RoundRectCard(Padding(
|
||||
padding: roundRectCardPadding,
|
||||
child: SizedBox(
|
||||
@@ -329,8 +330,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
||||
Icons.device_hub,
|
||||
size: 17,
|
||||
),
|
||||
Icon(Icons.arrow_downward, size: 17),
|
||||
Icon(Icons.arrow_upward, size: 17),
|
||||
Icon(Icons.arrow_downward, size: 17)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -190,14 +190,28 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: const Icon(Icons.send),
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
if (ipController.text == '') {
|
||||
showSnackBar(context, Text(s.plzEnterHost));
|
||||
return;
|
||||
}
|
||||
if (!usePublicKey && passwordController.text == '') {
|
||||
showSnackBar(context, Text(s.plzEnterPwd));
|
||||
return;
|
||||
final cancel = await showRoundDialog<bool>(
|
||||
context,
|
||||
s.attention,
|
||||
Text(s.sureNoPwd),
|
||||
[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: Text(s.ok)),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: Text(s.cancel))
|
||||
],
|
||||
barrierDismiss: false);
|
||||
if (cancel ?? true) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (usePublicKey && _pubKeyIndex == -1) {
|
||||
showSnackBar(context, Text(s.plzSelectKey));
|
||||
|
||||
@@ -93,6 +93,7 @@ flutter:
|
||||
- assets/linux/arch.png
|
||||
- assets/linux/fedora.png
|
||||
- assets/linux/opensuse.png
|
||||
- assets/linux/wrt.png
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
|
||||
Reference in New Issue
Block a user