feat & fix

- support openwrt
- save server info failed if connect error
- support ssh 'none' auth
This commit is contained in:
Junyuan Feng
2022-05-29 18:34:55 +08:00
parent 3ed476275f
commit bb1bf9219c
12 changed files with 70 additions and 46 deletions

BIN
assets/linux/wrt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -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});
}

View File

@@ -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 {

View File

@@ -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'
]);

View File

@@ -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"),

View File

@@ -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("未知"),

View File

@@ -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: [],
);

View File

@@ -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",

View File

@@ -73,7 +73,7 @@
"addPrivateKey": "添加一个私钥",
"choosePrivateKey": "选择私钥",
"plzEnterHost": "请输入主机",
"plzEnterPwd": "请输入密码",
"sureNoPwd": "确认使用无密码",
"plzSelectKey": "请选择私钥",
"exampleName": "名称示例",
"stop": "停止",

View File

@@ -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)
],
),
);

View File

@@ -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));

View File

@@ -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