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