mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
refactors (#539)
This commit is contained in:
@@ -58,7 +58,7 @@ class AppRoutes {
|
|||||||
return Future.value(null);
|
return Future.value(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes serverDetail({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes serverDetail({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(ServerDetailPage(key: key, spi: spi), 'server_detail');
|
return AppRoutes(ServerDetailPage(key: key, spi: spi), 'server_detail');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(ServerPage(key: key), 'server_tab');
|
return AppRoutes(ServerPage(key: key), 'server_tab');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes serverEdit({Key? key, ServerPrivateInfo? spi}) {
|
static AppRoutes serverEdit({Key? key, Spi? spi}) {
|
||||||
return AppRoutes(
|
return AppRoutes(
|
||||||
ServerEditPage(spi: spi),
|
ServerEditPage(spi: spi),
|
||||||
'server_${spi == null ? 'add' : 'edit'}',
|
'server_${spi == null ? 'add' : 'edit'}',
|
||||||
@@ -97,7 +97,7 @@ class AppRoutes {
|
|||||||
|
|
||||||
static AppRoutes ssh({
|
static AppRoutes ssh({
|
||||||
Key? key,
|
Key? key,
|
||||||
required ServerPrivateInfo spi,
|
required Spi spi,
|
||||||
String? initCmd,
|
String? initCmd,
|
||||||
Snippet? initSnippet,
|
Snippet? initSnippet,
|
||||||
}) {
|
}) {
|
||||||
@@ -133,7 +133,7 @@ class AppRoutes {
|
|||||||
|
|
||||||
static AppRoutes sftp(
|
static AppRoutes sftp(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required ServerPrivateInfo spi,
|
required Spi spi,
|
||||||
String? initPath,
|
String? initPath,
|
||||||
bool isSelect = false}) {
|
bool isSelect = false}) {
|
||||||
return AppRoutes(
|
return AppRoutes(
|
||||||
@@ -150,7 +150,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(BackupPage(key: key), 'backup');
|
return AppRoutes(BackupPage(key: key), 'backup');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes docker({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes docker({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(ContainerPage(key: key, spi: spi), 'docker');
|
return AppRoutes(ContainerPage(key: key, spi: spi), 'docker');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(PingPage(key: key), 'ping');
|
return AppRoutes(PingPage(key: key), 'ping');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes process({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes process({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(ProcessPage(key: key, spi: spi), 'process');
|
return AppRoutes(ProcessPage(key: key, spi: spi), 'process');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ class AppRoutes {
|
|||||||
'snippet_result');
|
'snippet_result');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes iperf({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes iperf({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(IPerfPage(key: key, spi: spi), 'iperf');
|
return AppRoutes(IPerfPage(key: key, spi: spi), 'iperf');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(ServerFuncBtnsOrderPage(key: key), 'server_func_btns_seq');
|
return AppRoutes(ServerFuncBtnsOrderPage(key: key), 'server_func_btns_seq');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes pve({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes pve({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(PvePage(key: key, spi: spi), 'pve');
|
return AppRoutes(PvePage(key: key, spi: spi), 'pve');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ String getPrivateKey(String id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<SSHClient> genClient(
|
Future<SSHClient> genClient(
|
||||||
ServerPrivateInfo spi, {
|
Spi spi, {
|
||||||
void Function(GenSSHClientStatus)? onStatus,
|
void Function(GenSSHClientStatus)? onStatus,
|
||||||
|
|
||||||
/// Only pass this param if using multi-threading and key login
|
/// Only pass this param if using multi-threading and key login
|
||||||
@@ -52,10 +52,10 @@ Future<SSHClient> genClient(
|
|||||||
String? jumpPrivateKey,
|
String? jumpPrivateKey,
|
||||||
Duration timeout = const Duration(seconds: 5),
|
Duration timeout = const Duration(seconds: 5),
|
||||||
|
|
||||||
/// [ServerPrivateInfo] of the jump server
|
/// [Spi] of the jump server
|
||||||
///
|
///
|
||||||
/// Must pass this param if using multi-threading and key login
|
/// Must pass this param if using multi-threading and key login
|
||||||
ServerPrivateInfo? jumpSpi,
|
Spi? jumpSpi,
|
||||||
|
|
||||||
/// Handle keyboard-interactive authentication
|
/// Handle keyboard-interactive authentication
|
||||||
FutureOr<List<String>?> Function(SSHUserInfoRequest)? onKeyboardInteractive,
|
FutureOr<List<String>?> Function(SSHUserInfoRequest)? onKeyboardInteractive,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ 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(
|
||||||
ServerPrivateInfo spi, {
|
Spi spi, {
|
||||||
BuildContext? ctx,
|
BuildContext? ctx,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import 'package:logging/logging.dart';
|
|||||||
import 'package:server_box/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.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/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
|
import 'package:server_box/data/provider/base.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/data/res/rebuild.dart';
|
import 'package:server_box/data/res/rebuild.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ class Backup {
|
|||||||
// backup format version
|
// backup format version
|
||||||
final int version;
|
final int version;
|
||||||
final String date;
|
final String date;
|
||||||
final List<ServerPrivateInfo> spis;
|
final List<Spi> spis;
|
||||||
final List<Snippet> snippets;
|
final List<Snippet> snippets;
|
||||||
final List<PrivateKeyInfo> keys;
|
final List<PrivateKeyInfo> keys;
|
||||||
final Map<String, dynamic> container;
|
final Map<String, dynamic> container;
|
||||||
@@ -177,7 +177,7 @@ class Backup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pros.reload();
|
Provider.reload();
|
||||||
RNodes.app.notify();
|
RNodes.app.notify();
|
||||||
|
|
||||||
_logger.info('Restore success');
|
_logger.info('Restore success');
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Backup _$BackupFromJson(Map<String, dynamic> json) => Backup(
|
|||||||
version: (json['version'] as num).toInt(),
|
version: (json['version'] as num).toInt(),
|
||||||
date: json['date'] as String,
|
date: json['date'] as String,
|
||||||
spis: (json['spis'] as List<dynamic>)
|
spis: (json['spis'] as List<dynamic>)
|
||||||
.map((e) => ServerPrivateInfo.fromJson(e as Map<String, dynamic>))
|
.map((e) => Spi.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
snippets: (json['snippets'] as List<dynamic>)
|
snippets: (json['snippets'] as List<dynamic>)
|
||||||
.map((e) => Snippet.fromJson(e as Map<String, dynamic>))
|
.map((e) => Snippet.fromJson(e as Map<String, dynamic>))
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import 'package:server_box/data/model/server/system.dart';
|
|||||||
import 'package:server_box/data/model/server/temp.dart';
|
import 'package:server_box/data/model/server/temp.dart';
|
||||||
|
|
||||||
class Server implements TagPickable {
|
class Server implements TagPickable {
|
||||||
ServerPrivateInfo spi;
|
Spi spi;
|
||||||
ServerStatus status;
|
ServerStatus status;
|
||||||
SSHClient? client;
|
SSHClient? client;
|
||||||
ServerConn conn;
|
ServerConn conn;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:server_box/data/model/server/custom.dart';
|
import 'package:server_box/data/model/server/custom.dart';
|
||||||
import 'package:server_box/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:server_box/data/model/server/wol_cfg.dart';
|
import 'package:server_box/data/model/server/wol_cfg.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
|
||||||
import 'package:server_box/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
|
|
||||||
@@ -16,10 +17,10 @@ part 'server_private_info.g.dart';
|
|||||||
///
|
///
|
||||||
/// Some params named as `spi` in the codebase which is the abbreviation of `ServerPrivateInfo`.
|
/// Some params named as `spi` in the codebase which is the abbreviation of `ServerPrivateInfo`.
|
||||||
///
|
///
|
||||||
/// Nowaday, more fields are added to this class, but the name is still the same.
|
/// Nowaday, more fields are added to this class, and it's renamed to `Spi`.
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@HiveType(typeId: 3)
|
@HiveType(typeId: 3)
|
||||||
class ServerPrivateInfo {
|
class Spi {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String name;
|
final String name;
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
@@ -58,7 +59,7 @@ class ServerPrivateInfo {
|
|||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
const ServerPrivateInfo({
|
const Spi({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.ip,
|
required this.ip,
|
||||||
required this.port,
|
required this.port,
|
||||||
@@ -74,17 +75,22 @@ class ServerPrivateInfo {
|
|||||||
this.envs,
|
this.envs,
|
||||||
}) : id = '$user@$ip:$port';
|
}) : id = '$user@$ip:$port';
|
||||||
|
|
||||||
factory ServerPrivateInfo.fromJson(Map<String, dynamic> json) =>
|
factory Spi.fromJson(Map<String, dynamic> json) =>
|
||||||
_$ServerPrivateInfoFromJson(json);
|
_$SpiFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$ServerPrivateInfoToJson(this);
|
Map<String, dynamic> toJson() => _$SpiToJson(this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => id;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Spix on Spi {
|
||||||
String toJsonString() => json.encode(toJson());
|
String toJsonString() => json.encode(toJson());
|
||||||
|
|
||||||
Server? get server => Pros.server.pick(spi: this);
|
VNode<Server>? get server => ServerProvider.pick(spi: this);
|
||||||
Server? get jumpServer => Pros.server.pick(id: jumpId);
|
VNode<Server>? get jumpServer => ServerProvider.pick(id: jumpId);
|
||||||
|
|
||||||
bool shouldReconnect(ServerPrivateInfo old) {
|
bool shouldReconnect(Spi old) {
|
||||||
return id != old.id ||
|
return id != old.id ||
|
||||||
pwd != old.pwd ||
|
pwd != old.pwd ||
|
||||||
keyId != old.keyId ||
|
keyId != old.keyId ||
|
||||||
@@ -113,12 +119,7 @@ class ServerPrivateInfo {
|
|||||||
return (ip_, port_);
|
return (ip_, port_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
static const example = Spi(
|
||||||
String toString() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const example = ServerPrivateInfo(
|
|
||||||
name: 'name',
|
name: 'name',
|
||||||
ip: 'ip',
|
ip: 'ip',
|
||||||
port: 22,
|
port: 22,
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ part of 'server_private_info.dart';
|
|||||||
// TypeAdapterGenerator
|
// TypeAdapterGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
class SpiAdapter extends TypeAdapter<Spi> {
|
||||||
@override
|
@override
|
||||||
final int typeId = 3;
|
final int typeId = 3;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ServerPrivateInfo read(BinaryReader reader) {
|
Spi read(BinaryReader reader) {
|
||||||
final numOfFields = reader.readByte();
|
final numOfFields = reader.readByte();
|
||||||
final fields = <int, dynamic>{
|
final fields = <int, dynamic>{
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return ServerPrivateInfo(
|
return Spi(
|
||||||
name: fields[0] as String,
|
name: fields[0] as String,
|
||||||
ip: fields[1] as String,
|
ip: fields[1] as String,
|
||||||
port: fields[2] as int,
|
port: fields[2] as int,
|
||||||
@@ -34,7 +34,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, ServerPrivateInfo obj) {
|
void write(BinaryWriter writer, Spi obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(13)
|
..writeByte(13)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
@@ -71,7 +71,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
other is ServerPrivateInfoAdapter &&
|
other is SpiAdapter &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
@@ -80,8 +80,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
ServerPrivateInfo _$ServerPrivateInfoFromJson(Map<String, dynamic> json) =>
|
Spi _$SpiFromJson(Map<String, dynamic> json) => Spi(
|
||||||
ServerPrivateInfo(
|
|
||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
ip: json['ip'] as String,
|
ip: json['ip'] as String,
|
||||||
port: (json['port'] as num).toInt(),
|
port: (json['port'] as num).toInt(),
|
||||||
@@ -103,8 +102,7 @@ ServerPrivateInfo _$ServerPrivateInfoFromJson(Map<String, dynamic> json) =>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$ServerPrivateInfoToJson(ServerPrivateInfo instance) =>
|
Map<String, dynamic> _$SpiToJson(Spi instance) => <String, dynamic>{
|
||||||
<String, dynamic>{
|
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'ip': instance.ip,
|
'ip': instance.ip,
|
||||||
'port': instance.port,
|
'port': instance.port,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class Snippet implements TagPickable {
|
|||||||
|
|
||||||
static final fmtFinder = RegExp(r'\$\{[^{}]+\}');
|
static final fmtFinder = RegExp(r'\$\{[^{}]+\}');
|
||||||
|
|
||||||
String fmtWithSpi(ServerPrivateInfo spi) {
|
String fmtWithSpi(Spi spi) {
|
||||||
return script.replaceAllMapped(
|
return script.replaceAllMapped(
|
||||||
fmtFinder,
|
fmtFinder,
|
||||||
(match) {
|
(match) {
|
||||||
@@ -63,7 +63,7 @@ class Snippet implements TagPickable {
|
|||||||
|
|
||||||
Future<void> runInTerm(
|
Future<void> runInTerm(
|
||||||
Terminal terminal,
|
Terminal terminal,
|
||||||
ServerPrivateInfo spi, {
|
Spi spi, {
|
||||||
bool autoEnter = false,
|
bool autoEnter = false,
|
||||||
}) async {
|
}) async {
|
||||||
final argsFmted = fmtWithSpi(spi);
|
final argsFmted = fmtWithSpi(spi);
|
||||||
@@ -159,12 +159,12 @@ class Snippet implements TagPickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final fmtArgs = {
|
static final fmtArgs = {
|
||||||
r'${host}': (ServerPrivateInfo spi) => spi.ip,
|
r'${host}': (Spi spi) => spi.ip,
|
||||||
r'${port}': (ServerPrivateInfo spi) => spi.port.toString(),
|
r'${port}': (Spi spi) => spi.port.toString(),
|
||||||
r'${user}': (ServerPrivateInfo spi) => spi.user,
|
r'${user}': (Spi spi) => spi.user,
|
||||||
r'${pwd}': (ServerPrivateInfo spi) => spi.pwd ?? '',
|
r'${pwd}': (Spi spi) => spi.pwd ?? '',
|
||||||
r'${id}': (ServerPrivateInfo spi) => spi.id,
|
r'${id}': (Spi spi) => spi.id,
|
||||||
r'${name}': (ServerPrivateInfo spi) => spi.name,
|
r'${name}': (Spi spi) => spi.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// r'${ctrl+ad}' -> TerminalKey.control, a, d
|
/// r'${ctrl+ad}' -> TerminalKey.control, a, d
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
part of 'worker.dart';
|
part of 'worker.dart';
|
||||||
|
|
||||||
class SftpReq {
|
class SftpReq {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
final String remotePath;
|
final String remotePath;
|
||||||
final String localPath;
|
final String localPath;
|
||||||
final SftpReqType type;
|
final SftpReqType type;
|
||||||
String? privateKey;
|
String? privateKey;
|
||||||
ServerPrivateInfo? jumpSpi;
|
Spi? jumpSpi;
|
||||||
String? jumpPrivateKey;
|
String? jumpPrivateKey;
|
||||||
|
|
||||||
SftpReq(
|
SftpReq(
|
||||||
|
|||||||
20
lib/data/provider/base.dart
Normal file
20
lib/data/provider/base.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
abstract class Provider<T> {
|
||||||
|
const Provider();
|
||||||
|
|
||||||
|
/// (Re)Load data from store / network / etc.
|
||||||
|
@mustCallSuper
|
||||||
|
FutureOr<void> load() {
|
||||||
|
all.add(this);
|
||||||
|
debugPrint('$runtimeType added');
|
||||||
|
}
|
||||||
|
|
||||||
|
static final all = <Provider>[];
|
||||||
|
|
||||||
|
static Future<void> reload() {
|
||||||
|
return Future.wait(all.map((e) async => await e.load()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +1,40 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
|
import 'package:server_box/data/provider/base.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class PrivateKeyProvider extends ChangeNotifier {
|
class PrivateKeyProvider extends Provider {
|
||||||
List<PrivateKeyInfo> get pkis => _pkis;
|
const PrivateKeyProvider._();
|
||||||
late List<PrivateKeyInfo> _pkis;
|
static const instance = PrivateKeyProvider._();
|
||||||
|
|
||||||
|
static final pkis = <PrivateKeyInfo>[].vn;
|
||||||
|
|
||||||
|
@override
|
||||||
void load() {
|
void load() {
|
||||||
_pkis = Stores.key.fetch();
|
super.load();
|
||||||
|
pkis.value = Stores.key.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(PrivateKeyInfo info) {
|
static void add(PrivateKeyInfo info) {
|
||||||
_pkis.add(info);
|
pkis.value.add(info);
|
||||||
|
pkis.notify();
|
||||||
Stores.key.put(info);
|
Stores.key.put(info);
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void delete(PrivateKeyInfo info) {
|
static void delete(PrivateKeyInfo info) {
|
||||||
_pkis.removeWhere((e) => e.id == info.id);
|
pkis.value.removeWhere((e) => e.id == info.id);
|
||||||
|
pkis.notify();
|
||||||
Stores.key.delete(info);
|
Stores.key.delete(info);
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(PrivateKeyInfo old, PrivateKeyInfo newInfo) {
|
static void update(PrivateKeyInfo old, PrivateKeyInfo newInfo) {
|
||||||
final idx = _pkis.indexWhere((e) => e.id == old.id);
|
final idx = pkis.value.indexWhere((e) => e.id == old.id);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
_pkis.add(newInfo);
|
pkis.value.add(newInfo);
|
||||||
} else {
|
} else {
|
||||||
_pkis[idx] = newInfo;
|
pkis.value[idx] = newInfo;
|
||||||
}
|
}
|
||||||
|
pkis.notify();
|
||||||
Stores.key.put(newInfo);
|
Stores.key.put(newInfo);
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import 'package:dartssh2/dartssh2.dart';
|
|||||||
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
||||||
|
|
||||||
final class PveProvider extends ChangeNotifier {
|
final class PveProvider extends ChangeNotifier {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
late String addr;
|
late String addr;
|
||||||
late final SSHClient _client;
|
late final SSHClient _client;
|
||||||
late final ServerSocket _serverSocket;
|
late final ServerSocket _serverSocket;
|
||||||
@@ -23,7 +23,7 @@ final class PveProvider extends ChangeNotifier {
|
|||||||
int _localPort = 0;
|
int _localPort = 0;
|
||||||
|
|
||||||
PveProvider({required this.spi}) {
|
PveProvider({required this.spi}) {
|
||||||
final client = spi.server?.client;
|
final client = spi.server?.value.client;
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
throw Exception('Server client is null');
|
throw Exception('Server client is null');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import 'dart:async';
|
|||||||
import 'package:computer/computer.dart';
|
import 'package:computer/computer.dart';
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:server_box/core/extension/ssh_client.dart';
|
import 'package:server_box/core/extension/ssh_client.dart';
|
||||||
import 'package:server_box/core/utils/ssh_auth.dart';
|
import 'package:server_box/core/utils/ssh_auth.dart';
|
||||||
import 'package:server_box/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:server_box/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
|
import 'package:server_box/data/provider/base.dart';
|
||||||
// import 'package:server_box/data/model/sftp/req.dart';
|
// import 'package:server_box/data/model/sftp/req.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';
|
||||||
@@ -21,24 +21,27 @@ import 'package:server_box/data/model/server/server_status_update_req.dart';
|
|||||||
import 'package:server_box/data/model/server/try_limiter.dart';
|
import 'package:server_box/data/model/server/try_limiter.dart';
|
||||||
import 'package:server_box/data/res/status.dart';
|
import 'package:server_box/data/res/status.dart';
|
||||||
|
|
||||||
class ServerProvider extends ChangeNotifier {
|
class ServerProvider extends Provider {
|
||||||
final Map<String, Server> _servers = {};
|
const ServerProvider._();
|
||||||
Iterable<Server> get servers => _servers.values;
|
static const instance = ServerProvider._();
|
||||||
final List<String> _serverOrder = [];
|
|
||||||
List<String> get serverOrder => _serverOrder;
|
|
||||||
final _tags = ValueNotifier(<String>{});
|
|
||||||
ValueNotifier<Set<String>> get tags => _tags;
|
|
||||||
|
|
||||||
Timer? _timer;
|
static final Map<String, VNode<Server>> servers = {};
|
||||||
|
static final serverOrder = <String>[].vn;
|
||||||
|
static final _tags = <String>{}.vn;
|
||||||
|
static VNode<Set<String>> get tags => _tags;
|
||||||
|
|
||||||
final _manualDisconnectedIds = <String>{};
|
static Timer? _timer;
|
||||||
|
|
||||||
|
static final _manualDisconnectedIds = <String>{};
|
||||||
|
|
||||||
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
|
super.load();
|
||||||
// #147
|
// #147
|
||||||
// Clear all servers because of restarting app will cause duplicate servers
|
// Clear all servers because of restarting app will cause duplicate servers
|
||||||
final oldServers = Map<String, Server>.from(_servers);
|
final oldServers = Map<String, VNode<Server>>.from(servers);
|
||||||
_servers.clear();
|
servers.clear();
|
||||||
_serverOrder.clear();
|
serverOrder.value.clear();
|
||||||
|
|
||||||
final spis = Stores.server.fetch();
|
final spis = Stores.server.fetch();
|
||||||
for (int idx = 0; idx < spis.length; idx++) {
|
for (int idx = 0; idx < spis.length; idx++) {
|
||||||
@@ -48,10 +51,11 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
/// #258
|
/// #258
|
||||||
/// If not [shouldReconnect], then keep the old state.
|
/// If not [shouldReconnect], then keep the old state.
|
||||||
if (originServer != null && !originServer.spi.shouldReconnect(spi)) {
|
if (originServer != null &&
|
||||||
newServer.conn = originServer.conn;
|
!originServer.value.spi.shouldReconnect(spi)) {
|
||||||
|
newServer.conn = originServer.value.conn;
|
||||||
}
|
}
|
||||||
_servers[spi.id] = newServer;
|
servers[spi.id] = newServer.vn;
|
||||||
}
|
}
|
||||||
final serverOrder_ = Stores.setting.serverOrder.fetch();
|
final serverOrder_ = Stores.setting.serverOrder.fetch();
|
||||||
if (serverOrder_.isNotEmpty) {
|
if (serverOrder_.isNotEmpty) {
|
||||||
@@ -59,35 +63,37 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
order: serverOrder_,
|
order: serverOrder_,
|
||||||
finder: (n, id) => n.id == id,
|
finder: (n, id) => n.id == id,
|
||||||
);
|
);
|
||||||
_serverOrder.addAll(spis.map((e) => e.id));
|
serverOrder.value.addAll(spis.map((e) => e.id));
|
||||||
} else {
|
} else {
|
||||||
_serverOrder.addAll(_servers.keys);
|
serverOrder.value.addAll(servers.keys);
|
||||||
}
|
}
|
||||||
// Must use [equals] to compare [Order] here.
|
// Must use [equals] to compare [Order] here.
|
||||||
if (!_serverOrder.equals(serverOrder_)) {
|
if (!serverOrder.value.equals(serverOrder_)) {
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
}
|
}
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
// Must notify here, or the UI will not be updated.
|
||||||
|
serverOrder.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a [Server] by [spi] or [id].
|
/// Get a [Server] by [spi] or [id].
|
||||||
///
|
///
|
||||||
/// Priority: [spi] > [id]
|
/// Priority: [spi] > [id]
|
||||||
Server? pick({ServerPrivateInfo? spi, String? id}) {
|
static VNode<Server>? pick({Spi? spi, String? id}) {
|
||||||
if (spi != null) {
|
if (spi != null) {
|
||||||
return _servers[spi.id];
|
return servers[spi.id];
|
||||||
}
|
}
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
return _servers[id];
|
return servers[id];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateTags() {
|
static void _updateTags() {
|
||||||
for (final s in _servers.values) {
|
for (final s in servers.values) {
|
||||||
if (s.spi.tags == null) continue;
|
final tags = s.value.spi.tags;
|
||||||
for (final t in s.spi.tags!) {
|
if (tags == null) continue;
|
||||||
|
for (final t in tags) {
|
||||||
if (!_tags.value.contains(t)) {
|
if (!_tags.value.contains(t)) {
|
||||||
_tags.value.add(t);
|
_tags.value.add(t);
|
||||||
}
|
}
|
||||||
@@ -96,14 +102,14 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
_tags.value = (_tags.value.toList()..sort()).toSet();
|
_tags.value = (_tags.value.toList()..sort()).toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
Server genServer(ServerPrivateInfo spi) {
|
static Server genServer(Spi spi) {
|
||||||
return Server(spi, InitStatus.status, ServerConn.disconnected);
|
return Server(spi, InitStatus.status, ServerConn.disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if [spi] is specificed then only refresh this server
|
/// if [spi] is specificed then only refresh this server
|
||||||
/// [onlyFailed] only refresh failed servers
|
/// [onlyFailed] only refresh failed servers
|
||||||
Future<void> refresh({
|
static Future<void> refresh({
|
||||||
ServerPrivateInfo? spi,
|
Spi? spi,
|
||||||
bool onlyFailed = false,
|
bool onlyFailed = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (spi != null) {
|
if (spi != null) {
|
||||||
@@ -112,7 +118,8 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(_servers.values.map((s) async {
|
await Future.wait(servers.values.map((val) async {
|
||||||
|
final s = val.value;
|
||||||
if (onlyFailed) {
|
if (onlyFailed) {
|
||||||
if (s.conn != ServerConn.failed) return;
|
if (s.conn != ServerConn.failed) return;
|
||||||
TryLimiter.reset(s.spi.id);
|
TryLimiter.reset(s.spi.id);
|
||||||
@@ -128,7 +135,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startAutoRefresh() async {
|
static Future<void> startAutoRefresh() async {
|
||||||
var duration = Stores.setting.serverStatusUpdateInterval.fetch();
|
var duration = Stores.setting.serverStatusUpdateInterval.fetch();
|
||||||
stopAutoRefresh();
|
stopAutoRefresh();
|
||||||
if (duration == 0) return;
|
if (duration == 0) return;
|
||||||
@@ -141,84 +148,85 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopAutoRefresh() {
|
static void stopAutoRefresh() {
|
||||||
if (_timer != null) {
|
if (_timer != null) {
|
||||||
_timer!.cancel();
|
_timer!.cancel();
|
||||||
_timer = null;
|
_timer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isAutoRefreshOn => _timer != null;
|
static bool get isAutoRefreshOn => _timer != null;
|
||||||
|
|
||||||
void setDisconnected() {
|
static void setDisconnected() {
|
||||||
for (final s in _servers.values) {
|
for (final s in servers.values) {
|
||||||
s.conn = ServerConn.disconnected;
|
s.value.conn = ServerConn.disconnected;
|
||||||
|
s.notify();
|
||||||
}
|
}
|
||||||
//TryLimiter.clear();
|
//TryLimiter.clear();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeServer({String? id}) {
|
static void closeServer({String? id}) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
for (final s in _servers.values) {
|
for (final s in servers.values) {
|
||||||
_closeOneServer(s.spi.id);
|
_closeOneServer(s.value.spi.id);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_closeOneServer(id);
|
_closeOneServer(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _closeOneServer(String id) {
|
static void _closeOneServer(String id) {
|
||||||
final item = _servers[id];
|
final s = servers[id];
|
||||||
|
final item = s?.value;
|
||||||
item?.client?.close();
|
item?.client?.close();
|
||||||
item?.client = null;
|
item?.client = null;
|
||||||
item?.conn = ServerConn.disconnected;
|
item?.conn = ServerConn.disconnected;
|
||||||
_manualDisconnectedIds.add(id);
|
_manualDisconnectedIds.add(id);
|
||||||
notifyListeners();
|
s?.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addServer(ServerPrivateInfo spi) {
|
static void addServer(Spi spi) {
|
||||||
_servers[spi.id] = genServer(spi);
|
servers[spi.id] = genServer(spi).vn;
|
||||||
notifyListeners();
|
|
||||||
Stores.server.put(spi);
|
Stores.server.put(spi);
|
||||||
_serverOrder.add(spi.id);
|
serverOrder.value.add(spi.id);
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
serverOrder.notify();
|
||||||
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
_updateTags();
|
_updateTags();
|
||||||
refresh(spi: spi);
|
refresh(spi: spi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void delServer(String id) {
|
static void delServer(String id) {
|
||||||
_servers.remove(id);
|
servers.remove(id);
|
||||||
_serverOrder.remove(id);
|
serverOrder.value.remove(id);
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
serverOrder.notify();
|
||||||
_updateTags();
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
notifyListeners();
|
|
||||||
Stores.server.delete(id);
|
Stores.server.delete(id);
|
||||||
}
|
|
||||||
|
|
||||||
void deleteAll() {
|
|
||||||
_servers.clear();
|
|
||||||
_serverOrder.clear();
|
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
Stores.server.deleteAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateServer(
|
static void deleteAll() {
|
||||||
ServerPrivateInfo old,
|
servers.clear();
|
||||||
ServerPrivateInfo newSpi,
|
serverOrder.value.clear();
|
||||||
|
serverOrder.notify();
|
||||||
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
|
Stores.server.deleteAll();
|
||||||
|
_updateTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> updateServer(
|
||||||
|
Spi old,
|
||||||
|
Spi newSpi,
|
||||||
) async {
|
) async {
|
||||||
if (old != newSpi) {
|
if (old != newSpi) {
|
||||||
Stores.server.update(old, newSpi);
|
Stores.server.update(old, newSpi);
|
||||||
_servers[old.id]?.spi = newSpi;
|
servers[old.id]?.value.spi = newSpi;
|
||||||
|
|
||||||
if (newSpi.id != old.id) {
|
if (newSpi.id != old.id) {
|
||||||
_servers[newSpi.id] = _servers[old.id]!;
|
servers[newSpi.id] = servers[old.id]!;
|
||||||
_servers[newSpi.id]?.spi = newSpi;
|
servers.remove(old.id);
|
||||||
_servers.remove(old.id);
|
serverOrder.value.update(old.id, newSpi.id);
|
||||||
_serverOrder.update(old.id, newSpi.id);
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
serverOrder.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only reconnect if neccessary
|
// Only reconnect if neccessary
|
||||||
@@ -227,33 +235,32 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
TryLimiter.reset(newSpi.id);
|
TryLimiter.reset(newSpi.id);
|
||||||
refresh(spi: newSpi);
|
refresh(spi: newSpi);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Only update if [spi.tags] changed
|
|
||||||
_updateTags();
|
_updateTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _setServerState(VNode<Server> s, ServerConn ss) {
|
||||||
|
s.value.conn = ss;
|
||||||
|
s.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setServerState(Server s, ServerConn ss) {
|
static Future<void> _getData(Spi spi) async {
|
||||||
s.conn = ss;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _getData(ServerPrivateInfo spi) async {
|
|
||||||
final sid = spi.id;
|
final sid = spi.id;
|
||||||
final s = _servers[sid];
|
final s = servers[sid];
|
||||||
|
|
||||||
if (s == null) return;
|
if (s == null) return;
|
||||||
|
|
||||||
|
final sv = s.value;
|
||||||
if (!TryLimiter.canTry(sid)) {
|
if (!TryLimiter.canTry(sid)) {
|
||||||
if (s.conn != ServerConn.failed) {
|
if (sv.conn != ServerConn.failed) {
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.status.err = null;
|
sv.status.err = null;
|
||||||
|
|
||||||
if (s.needGenClient || (s.client?.isClosed ?? true)) {
|
if (sv.needGenClient || (sv.client?.isClosed ?? true)) {
|
||||||
_setServerState(s, ServerConn.connecting);
|
_setServerState(s, ServerConn.connecting);
|
||||||
|
|
||||||
final wol = spi.wolCfg;
|
final wol = spi.wolCfg;
|
||||||
@@ -274,7 +281,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final time1 = DateTime.now();
|
final time1 = DateTime.now();
|
||||||
s.client = await genClient(
|
sv.client = await genClient(
|
||||||
spi,
|
spi,
|
||||||
timeout: Duration(seconds: Stores.setting.timeout.fetch()),
|
timeout: Duration(seconds: Stores.setting.timeout.fetch()),
|
||||||
onKeyboardInteractive: (_) => KeybordInteractive.defaultHandle(spi),
|
onKeyboardInteractive: (_) => KeybordInteractive.defaultHandle(spi),
|
||||||
@@ -288,7 +295,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(type: SSHErrType.connect, message: e.toString());
|
sv.status.err = SSHErr(type: SSHErrType.connect, message: e.toString());
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
|
|
||||||
/// In order to keep privacy, print [spi.name] instead of [spi.id]
|
/// In order to keep privacy, print [spi.name] instead of [spi.id]
|
||||||
@@ -301,7 +308,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
|
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final (_, writeScriptResult) = await s.client!.exec(
|
final (_, writeScriptResult) = await sv.client!.exec(
|
||||||
(session) async {
|
(session) async {
|
||||||
session.stdin.add(scriptRaw);
|
session.stdin.add(scriptRaw);
|
||||||
session.stdin.close();
|
session.stdin.close();
|
||||||
@@ -315,14 +322,14 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
} on SSHAuthAbortError catch (e) {
|
} on SSHAuthAbortError catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
final err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
final err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
||||||
s.status.err = err;
|
sv.status.err = err;
|
||||||
Loggers.app.warning(err);
|
Loggers.app.warning(err);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
} on SSHAuthFailError catch (e) {
|
} on SSHAuthFailError catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
final err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
final err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
||||||
s.status.err = err;
|
sv.status.err = err;
|
||||||
Loggers.app.warning(err);
|
Loggers.app.warning(err);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
@@ -330,18 +337,18 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
// If max try times < 2 and can't write script, this will stop the status getting and etc.
|
// If max try times < 2 and can't write script, this will stop the status getting and etc.
|
||||||
// TryLimiter.inc(sid);
|
// TryLimiter.inc(sid);
|
||||||
final err = SSHErr(type: SSHErrType.writeScript, message: e.toString());
|
final err = SSHErr(type: SSHErrType.writeScript, message: e.toString());
|
||||||
s.status.err = err;
|
sv.status.err = err;
|
||||||
Loggers.app.warning(err);
|
Loggers.app.warning(err);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.conn == ServerConn.connecting) return;
|
if (sv.conn == ServerConn.connecting) return;
|
||||||
|
|
||||||
/// Keep [finished] state, or the UI will be refreshed to [loading] state
|
/// Keep [finished] state, or the UI will be refreshed to [loading] state
|
||||||
/// instead of the '$Temp | $Uptime'.
|
/// instead of the '$Temp | $Uptime'.
|
||||||
/// eg: '32C | 7 days'
|
/// eg: '32C | 7 days'
|
||||||
if (s.conn != ServerConn.finished) {
|
if (sv.conn != ServerConn.finished) {
|
||||||
_setServerState(s, ServerConn.loading);
|
_setServerState(s, ServerConn.loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,17 +356,17 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
String? raw;
|
String? raw;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
raw = await s.client?.run(ShellFunc.status.exec(spi.id)).string;
|
raw = await sv.client?.run(ShellFunc.status.exec(spi.id)).string;
|
||||||
segments = raw?.split(ShellFunc.seperator).map((e) => e.trim()).toList();
|
segments = raw?.split(ShellFunc.seperator).map((e) => e.trim()).toList();
|
||||||
if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) {
|
if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) {
|
||||||
if (Stores.setting.keepStatusWhenErr.fetch()) {
|
if (Stores.setting.keepStatusWhenErr.fetch()) {
|
||||||
// Keep previous server status when err occurs
|
// Keep previous server status when err occurs
|
||||||
if (s.conn != ServerConn.failed && s.status.more.isNotEmpty) {
|
if (sv.conn != ServerConn.failed && sv.status.more.isNotEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(
|
sv.status.err = SSHErr(
|
||||||
type: SSHErrType.segements,
|
type: SSHErrType.segements,
|
||||||
message: 'Seperate segments failed, raw:\n$raw',
|
message: 'Seperate segments failed, raw:\n$raw',
|
||||||
);
|
);
|
||||||
@@ -368,7 +375,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(type: SSHErrType.getStatus, message: e.toString());
|
sv.status.err = SSHErr(type: SSHErrType.getStatus, message: e.toString());
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
Loggers.app.warning('Get status from ${spi.name} failed', e);
|
Loggers.app.warning('Get status from ${spi.name} failed', e);
|
||||||
return;
|
return;
|
||||||
@@ -379,36 +386,36 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
if (!systemType.isSegmentsLenMatch(segments.length - customCmdLen)) {
|
if (!systemType.isSegmentsLenMatch(segments.length - customCmdLen)) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
if (raw.contains('Could not chdir to home directory /var/services/')) {
|
if (raw.contains('Could not chdir to home directory /var/services/')) {
|
||||||
s.status.err = SSHErr(type: SSHErrType.chdir, message: raw);
|
sv.status.err = SSHErr(type: SSHErrType.chdir, message: raw);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final expected = systemType.segmentsLen;
|
final expected = systemType.segmentsLen;
|
||||||
final actual = segments.length;
|
final actual = segments.length;
|
||||||
s.status.err = SSHErr(
|
sv.status.err = SSHErr(
|
||||||
type: SSHErrType.segements,
|
type: SSHErrType.segements,
|
||||||
message: 'Segments: expect $expected, got $actual, raw:\n\n$raw',
|
message: 'Segments: expect $expected, got $actual, raw:\n\n$raw',
|
||||||
);
|
);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s.status.system = systemType;
|
sv.status.system = systemType;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final req = ServerStatusUpdateReq(
|
final req = ServerStatusUpdateReq(
|
||||||
ss: s.status,
|
ss: sv.status,
|
||||||
segments: segments,
|
segments: segments,
|
||||||
system: systemType,
|
system: systemType,
|
||||||
customCmds: spi.custom?.cmds ?? {},
|
customCmds: spi.custom?.cmds ?? {},
|
||||||
);
|
);
|
||||||
s.status = await Computer.shared.start(
|
sv.status = await Computer.shared.start(
|
||||||
getStatus,
|
getStatus,
|
||||||
req,
|
req,
|
||||||
taskName: 'StatusUpdateReq<${s.id}>',
|
taskName: 'StatusUpdateReq<${sv.id}>',
|
||||||
);
|
);
|
||||||
} catch (e, trace) {
|
} catch (e, trace) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(
|
sv.status.err = SSHErr(
|
||||||
type: SSHErrType.getStatus,
|
type: SSHErrType.getStatus,
|
||||||
message: 'Parse failed: $e\n\n$raw',
|
message: 'Parse failed: $e\n\n$raw',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,38 +1,42 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/data/model/sftp/worker.dart';
|
import 'package:server_box/data/model/sftp/worker.dart';
|
||||||
|
import 'package:server_box/data/provider/base.dart';
|
||||||
|
|
||||||
class SftpProvider extends ChangeNotifier {
|
class SftpProvider extends Provider {
|
||||||
final List<SftpReqStatus> _status = [];
|
const SftpProvider._();
|
||||||
List<SftpReqStatus> get status => _status;
|
static const instance = SftpProvider._();
|
||||||
|
|
||||||
SftpReqStatus? get(int id) {
|
static final status = <SftpReqStatus>[].vn;
|
||||||
return _status.singleWhere((element) => element.id == id);
|
|
||||||
|
static SftpReqStatus? get(int id) {
|
||||||
|
return status.value.singleWhere((element) => element.id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int add(SftpReq req, {Completer? completer}) {
|
static int add(SftpReq req, {Completer? completer}) {
|
||||||
final status = SftpReqStatus(
|
final reqStat = SftpReqStatus(
|
||||||
notifyListeners: notifyListeners,
|
notifyListeners: status.notify,
|
||||||
completer: completer,
|
completer: completer,
|
||||||
req: req,
|
req: req,
|
||||||
);
|
);
|
||||||
_status.add(status);
|
status.value.add(reqStat);
|
||||||
return status.id;
|
status.notify();
|
||||||
|
return reqStat.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
static void dispose() {
|
||||||
void dispose() {
|
for (final item in status.value) {
|
||||||
for (final item in _status) {
|
|
||||||
item.dispose();
|
item.dispose();
|
||||||
}
|
}
|
||||||
super.dispose();
|
status.value.clear();
|
||||||
|
status.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancel(int id) {
|
static void cancel(int id) {
|
||||||
final idx = _status.indexWhere((element) => element.id == id);
|
final idx = status.value.indexWhere((e) => e.id == id);
|
||||||
_status[idx].dispose();
|
status.value[idx].dispose();
|
||||||
_status.removeAt(idx);
|
status.value.removeAt(idx);
|
||||||
notifyListeners();
|
status.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
|
import 'package:server_box/data/provider/base.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class SnippetProvider extends ChangeNotifier {
|
class SnippetProvider extends Provider {
|
||||||
late List<Snippet> _snippets;
|
const SnippetProvider._();
|
||||||
List<Snippet> get snippets => _snippets;
|
static const instance = SnippetProvider._();
|
||||||
|
|
||||||
final tags = ValueNotifier(<String>{});
|
static final snippets = <Snippet>[].vn;
|
||||||
|
static final tags = <String>{}.vn;
|
||||||
|
|
||||||
|
@override
|
||||||
void load() {
|
void load() {
|
||||||
_snippets = Stores.snippet.fetch();
|
super.load();
|
||||||
|
final snippets_ = Stores.snippet.fetch();
|
||||||
final order = Stores.setting.snippetOrder.fetch();
|
final order = Stores.setting.snippetOrder.fetch();
|
||||||
if (order.isNotEmpty) {
|
if (order.isNotEmpty) {
|
||||||
final surplus = _snippets.reorder(
|
final surplus = snippets_.reorder(
|
||||||
order: order,
|
order: order,
|
||||||
finder: (n, name) => n.name == name,
|
finder: (n, name) => n.name == name,
|
||||||
);
|
);
|
||||||
@@ -24,12 +25,13 @@ class SnippetProvider extends ChangeNotifier {
|
|||||||
Stores.setting.snippetOrder.put(order);
|
Stores.setting.snippetOrder.put(order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
snippets.value = snippets_;
|
||||||
_updateTags();
|
_updateTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateTags() {
|
static void _updateTags() {
|
||||||
final tags_ = <String>{};
|
final tags_ = <String>{};
|
||||||
for (final s in _snippets) {
|
for (final s in snippets.value) {
|
||||||
final t = s.tags;
|
final t = s.tags;
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
tags_.addAll(t);
|
tags_.addAll(t);
|
||||||
@@ -38,31 +40,31 @@ class SnippetProvider extends ChangeNotifier {
|
|||||||
tags.value = tags_;
|
tags.value = tags_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(Snippet snippet) {
|
static void add(Snippet snippet) {
|
||||||
_snippets.add(snippet);
|
snippets.value.add(snippet);
|
||||||
|
snippets.notify();
|
||||||
Stores.snippet.put(snippet);
|
Stores.snippet.put(snippet);
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void del(Snippet snippet) {
|
static void del(Snippet snippet) {
|
||||||
_snippets.remove(snippet);
|
snippets.value.remove(snippet);
|
||||||
|
snippets.notify();
|
||||||
Stores.snippet.delete(snippet);
|
Stores.snippet.delete(snippet);
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(Snippet old, Snippet newOne) {
|
static void update(Snippet old, Snippet newOne) {
|
||||||
|
snippets.value.remove(old);
|
||||||
|
snippets.value.add(newOne);
|
||||||
|
snippets.notify();
|
||||||
Stores.snippet.delete(old);
|
Stores.snippet.delete(old);
|
||||||
Stores.snippet.put(newOne);
|
Stores.snippet.put(newOne);
|
||||||
_snippets.remove(old);
|
|
||||||
_snippets.add(newOne);
|
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void renameTag(String old, String newOne) {
|
static void renameTag(String old, String newOne) {
|
||||||
for (final s in _snippets) {
|
for (final s in snippets.value) {
|
||||||
if (s.tags?.contains(old) ?? false) {
|
if (s.tags?.contains(old) ?? false) {
|
||||||
s.tags?.remove(old);
|
s.tags?.remove(old);
|
||||||
s.tags?.add(newOne);
|
s.tags?.add(newOne);
|
||||||
@@ -70,8 +72,5 @@ class SnippetProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get export => json.encode(snippets);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import 'package:dartssh2/dartssh2.dart';
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/core/extension/ssh_client.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/app/shell_func.dart';
|
||||||
|
import 'package:server_box/data/model/server/server.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/model/server/systemd.dart';
|
import 'package:server_box/data/model/server/systemd.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
|
||||||
final class SystemdProvider {
|
final class SystemdProvider {
|
||||||
late final SSHClient _client;
|
late final VNode<Server> _si;
|
||||||
late final bool isRoot;
|
late final bool _isRoot;
|
||||||
|
|
||||||
SystemdProvider.init(ServerPrivateInfo spi) {
|
SystemdProvider.init(Spi spi) {
|
||||||
isRoot = spi.isRoot;
|
_isRoot = spi.isRoot;
|
||||||
_client = Pros.server.pick(spi: spi)!.client!;
|
_si = ServerProvider.pick(spi: spi)!;
|
||||||
getUnits();
|
getUnits();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ final class SystemdProvider {
|
|||||||
isBusy.value = true;
|
isBusy.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = await _client.execForOutput(_getUnitsCmd);
|
final client = _si.value.client;
|
||||||
|
final result = await client!.execForOutput(_getUnitsCmd);
|
||||||
final units = result.split('\n');
|
final units = result.split('\n');
|
||||||
|
|
||||||
final userUnits = <String>[];
|
final userUnits = <String>[];
|
||||||
@@ -63,7 +64,8 @@ for unit in ${unitNames_.join(' ')}; do
|
|||||||
echo -n "${ShellFunc.seperator}\n\$state"
|
echo -n "${ShellFunc.seperator}\n\$state"
|
||||||
done
|
done
|
||||||
''';
|
''';
|
||||||
final result = await _client.execForOutput(script);
|
final client = _si.value.client!;
|
||||||
|
final result = await client.execForOutput(script);
|
||||||
final units = result.split(ShellFunc.seperator);
|
final units = result.split(ShellFunc.seperator);
|
||||||
|
|
||||||
final parsedUnits = <SystemdUnit>[];
|
final parsedUnits = <SystemdUnit>[];
|
||||||
@@ -141,7 +143,7 @@ get_type_files() {
|
|||||||
unit_type=\$1
|
unit_type=\$1
|
||||||
base_dir=""
|
base_dir=""
|
||||||
|
|
||||||
${isRoot ? """
|
${_isRoot ? """
|
||||||
get_files \$unit_type /etc/systemd/system
|
get_files \$unit_type /etc/systemd/system
|
||||||
get_files \$unit_type ~/.config/systemd/user""" : """
|
get_files \$unit_type ~/.config/systemd/user""" : """
|
||||||
get_files \$unit_type ~/.config/systemd/user"""}
|
get_files \$unit_type ~/.config/systemd/user"""}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import 'package:server_box/data/provider/private_key.dart';
|
|
||||||
import 'package:server_box/data/provider/server.dart';
|
|
||||||
import 'package:server_box/data/provider/sftp.dart';
|
|
||||||
import 'package:server_box/data/provider/snippet.dart';
|
|
||||||
|
|
||||||
abstract final class Pros {
|
|
||||||
static final key = PrivateKeyProvider();
|
|
||||||
static final server = ServerProvider();
|
|
||||||
static final sftp = SftpProvider();
|
|
||||||
static final snippet = SnippetProvider();
|
|
||||||
|
|
||||||
static void reload() {
|
|
||||||
key.load();
|
|
||||||
server.load();
|
|
||||||
snippet.load();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,17 +5,17 @@ import 'package:server_box/data/model/server/server_private_info.dart';
|
|||||||
class ServerStore extends PersistentStore {
|
class ServerStore extends PersistentStore {
|
||||||
ServerStore() : super('server');
|
ServerStore() : super('server');
|
||||||
|
|
||||||
void put(ServerPrivateInfo info) {
|
void put(Spi info) {
|
||||||
box.put(info.id, info);
|
box.put(info.id, info);
|
||||||
box.updateLastModified();
|
box.updateLastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ServerPrivateInfo> fetch() {
|
List<Spi> fetch() {
|
||||||
final ids = box.keys;
|
final ids = box.keys;
|
||||||
final List<ServerPrivateInfo> ss = [];
|
final List<Spi> ss = [];
|
||||||
for (final id in ids) {
|
for (final id in ids) {
|
||||||
final s = box.get(id);
|
final s = box.get(id);
|
||||||
if (s != null && s is ServerPrivateInfo) {
|
if (s != null && s is Spi) {
|
||||||
ss.add(s);
|
ss.add(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ class ServerStore extends PersistentStore {
|
|||||||
box.updateLastModified();
|
box.updateLastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(ServerPrivateInfo old, ServerPrivateInfo newInfo) {
|
void update(Spi old, Spi newInfo) {
|
||||||
if (!have(old)) {
|
if (!have(old)) {
|
||||||
throw Exception('Old spi: $old not found');
|
throw Exception('Old spi: $old not found');
|
||||||
}
|
}
|
||||||
@@ -40,5 +40,5 @@ class ServerStore extends PersistentStore {
|
|||||||
put(newInfo);
|
put(newInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool have(ServerPrivateInfo s) => box.get(s.id) != null;
|
bool have(Spi s) => box.get(s.id) != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:server_box/app.dart';
|
import 'package:server_box/app.dart';
|
||||||
import 'package:server_box/core/utils/sync/icloud.dart';
|
import 'package:server_box/core/utils/sync/icloud.dart';
|
||||||
import 'package:server_box/core/utils/sync/webdav.dart';
|
import 'package:server_box/core/utils/sync/webdav.dart';
|
||||||
@@ -21,25 +20,18 @@ import 'package:server_box/data/model/server/server_private_info.dart';
|
|||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/model/server/wol_cfg.dart';
|
import 'package:server_box/data/model/server/wol_cfg.dart';
|
||||||
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
import 'package:server_box/data/model/ssh/virtual_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/sftp.dart';
|
||||||
|
import 'package:server_box/data/provider/snippet.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/misc.dart';
|
import 'package:server_box/data/res/misc.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';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
_runInZone(() async {
|
_runInZone(() async {
|
||||||
await _initApp();
|
await _initApp();
|
||||||
runApp(
|
runApp(const MyApp());
|
||||||
MultiProvider(
|
|
||||||
providers: [
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.server),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.snippet),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.key),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.sftp),
|
|
||||||
],
|
|
||||||
child: const MyApp(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +76,7 @@ Future<void> _initData() async {
|
|||||||
// Ordered by typeId
|
// Ordered by typeId
|
||||||
Hive.registerAdapter(PrivateKeyInfoAdapter()); // 1
|
Hive.registerAdapter(PrivateKeyInfoAdapter()); // 1
|
||||||
Hive.registerAdapter(SnippetAdapter()); // 2
|
Hive.registerAdapter(SnippetAdapter()); // 2
|
||||||
Hive.registerAdapter(ServerPrivateInfoAdapter()); // 3
|
Hive.registerAdapter(SpiAdapter()); // 3
|
||||||
Hive.registerAdapter(VirtKeyAdapter()); // 4
|
Hive.registerAdapter(VirtKeyAdapter()); // 4
|
||||||
Hive.registerAdapter(NetViewTypeAdapter()); // 5
|
Hive.registerAdapter(NetViewTypeAdapter()); // 5
|
||||||
Hive.registerAdapter(ServerFuncBtnAdapter()); // 6
|
Hive.registerAdapter(ServerFuncBtnAdapter()); // 6
|
||||||
@@ -94,8 +86,11 @@ Future<void> _initData() async {
|
|||||||
await PrefStore.init(); // Call this before accessing any store
|
await PrefStore.init(); // Call this before accessing any store
|
||||||
await Stores.init();
|
await Stores.init();
|
||||||
|
|
||||||
Pros.snippet.load();
|
// DO NOT change the order of these providers.
|
||||||
Pros.key.load();
|
PrivateKeyProvider.instance.load();
|
||||||
|
SnippetProvider.instance.load();
|
||||||
|
ServerProvider.instance.load();
|
||||||
|
SftpProvider.instance.load();
|
||||||
|
|
||||||
if (Stores.setting.betaTest.fetch()) AppUpdate.chan = AppUpdateChan.beta;
|
if (Stores.setting.betaTest.fetch()) AppUpdate.chan = AppUpdateChan.beta;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import 'package:server_box/core/utils/sync/webdav.dart';
|
|||||||
import 'package:server_box/data/model/app/backup.dart';
|
import 'package:server_box/data/model/app/backup.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/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.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:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
actions: Btn.ok(
|
actions: Btn.ok(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
for (final snippet in snippets) {
|
for (final snippet in snippets) {
|
||||||
Pros.snippet.add(snippet);
|
SnippetProvider.add(snippet);
|
||||||
}
|
}
|
||||||
context.pop();
|
context.pop();
|
||||||
context.pop();
|
context.pop();
|
||||||
@@ -441,7 +441,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
void _onBulkImportServers(BuildContext context) async {
|
void _onBulkImportServers(BuildContext context) async {
|
||||||
final data = await context.showImportDialog(
|
final data = await context.showImportDialog(
|
||||||
title: l10n.server,
|
title: l10n.server,
|
||||||
modelDef: ServerPrivateInfo.example.toJson(),
|
modelDef: Spix.example.toJson(),
|
||||||
);
|
);
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
final text = String.fromCharCodes(data);
|
final text = String.fromCharCodes(data);
|
||||||
@@ -450,7 +450,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
final (spis, err) = await context.showLoadingDialog(
|
final (spis, err) = await context.showLoadingDialog(
|
||||||
fn: () => Computer.shared.start((val) {
|
fn: () => Computer.shared.start((val) {
|
||||||
final list = json.decode(val) as List;
|
final list = json.decode(val) as List;
|
||||||
return list.map((e) => ServerPrivateInfo.fromJson(e)).toList();
|
return list.map((e) => Spi.fromJson(e)).toList();
|
||||||
}, text.trim()),
|
}, text.trim()),
|
||||||
);
|
);
|
||||||
if (err != null || spis == null) return;
|
if (err != null || spis == null) return;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import 'package:server_box/data/provider/container.dart';
|
|||||||
import 'package:server_box/view/widget/two_line_text.dart';
|
import 'package:server_box/view/widget/two_line_text.dart';
|
||||||
|
|
||||||
class ContainerPage extends StatefulWidget {
|
class ContainerPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
const ContainerPage({required this.spi, super.key});
|
const ContainerPage({required this.spi, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -27,7 +27,7 @@ class ContainerPage extends StatefulWidget {
|
|||||||
class _ContainerPageState extends State<ContainerPage> {
|
class _ContainerPageState extends State<ContainerPage> {
|
||||||
final _textController = TextEditingController();
|
final _textController = TextEditingController();
|
||||||
late final _container = ContainerProvider(
|
late final _container = ContainerProvider(
|
||||||
client: widget.spi.server?.client,
|
client: widget.spi.server?.value.client,
|
||||||
userName: widget.spi.user,
|
userName: widget.spi.user,
|
||||||
hostId: widget.spi.id,
|
hostId: widget.spi.id,
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ 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/provider/app.dart';
|
||||||
|
import 'package:server_box/data/provider/server.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';
|
||||||
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/data/res/url.dart';
|
import 'package:server_box/data/res/url.dart';
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
@@ -65,7 +65,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
Pros.server.closeServer();
|
ServerProvider.closeServer();
|
||||||
_pageController.dispose();
|
_pageController.dispose();
|
||||||
WakelockPlus.disable();
|
WakelockPlus.disable();
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,8 @@ class _HomePageState extends State<HomePage>
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
if (_shouldAuth) _goAuth();
|
if (_shouldAuth) _goAuth();
|
||||||
if (!Pros.server.isAutoRefreshOn) {
|
if (!ServerProvider.isAutoRefreshOn) {
|
||||||
Pros.server.startAutoRefresh();
|
ServerProvider.startAutoRefresh();
|
||||||
}
|
}
|
||||||
HomeWidgetMC.update();
|
HomeWidgetMC.update();
|
||||||
break;
|
break;
|
||||||
@@ -93,7 +93,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
//Pros.server.setDisconnected();
|
//Pros.server.setDisconnected();
|
||||||
Pros.server.stopAutoRefresh();
|
ServerProvider.stopAutoRefresh();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -120,7 +120,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
tooltip: 'Refresh',
|
tooltip: 'Refresh',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await Pros.server.refresh();
|
await ServerProvider.refresh();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -333,8 +333,7 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
HomeWidgetMC.update();
|
HomeWidgetMC.update();
|
||||||
await Pros.server.load();
|
await ServerProvider.refresh();
|
||||||
await Pros.server.refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> _reqNotiPerm() async {
|
// Future<void> _reqNotiPerm() async {
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:server_box/data/model/server/server.dart';
|
|
||||||
import 'package:server_box/data/provider/server.dart';
|
|
||||||
import 'package:server_box/data/res/build_data.dart';
|
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/data/res/store.dart';
|
|
||||||
import 'package:server_box/data/res/url.dart';
|
|
||||||
|
|
||||||
final class WearHome extends StatefulWidget {
|
|
||||||
const WearHome({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<WearHome> createState() => _WearHomeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
final class _WearHomeState extends State<WearHome> with AfterLayoutMixin {
|
|
||||||
late final _pageCtrl =
|
|
||||||
PageController(initialPage: Pros.server.servers.isNotEmpty ? 1 : 0);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _buildBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBody() {
|
|
||||||
return Consumer<ServerProvider>(builder: (_, pro, __) {
|
|
||||||
if (pro.servers.isEmpty) {
|
|
||||||
return const Center(child: Text('No server'));
|
|
||||||
}
|
|
||||||
return PageView.builder(
|
|
||||||
controller: _pageCtrl,
|
|
||||||
itemCount: pro.servers.length + 1,
|
|
||||||
itemBuilder: (_, index) {
|
|
||||||
if (index == 0) return _buildInit();
|
|
||||||
|
|
||||||
final id = pro.serverOrder[index];
|
|
||||||
final server = Pros.server.pick(id: id);
|
|
||||||
if (server == null) return UIs.placeholder;
|
|
||||||
return _buildEachSever(server);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildInit() {
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
|
|
||||||
UIs.height7,
|
|
||||||
Text(libL10n.restore)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildEachSever(Server srv) {
|
|
||||||
final mem = () {
|
|
||||||
final total = srv.status.mem.total;
|
|
||||||
final used = srv.status.mem.total - srv.status.mem.avail;
|
|
||||||
return '${used.bytes2Str} / ${total.bytes2Str}';
|
|
||||||
}();
|
|
||||||
final disk = () {
|
|
||||||
final total = srv.status.diskUsage?.size.kb2Str;
|
|
||||||
final used = srv.status.diskUsage?.used.kb2Str;
|
|
||||||
return '$used / $total';
|
|
||||||
}();
|
|
||||||
final net = '↓ ${srv.status.netSpeed.cachedRealVals.speedIn}'
|
|
||||||
'↑ ${srv.status.netSpeed.cachedRealVals.speedOut}';
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(7),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(srv.spi.name, style: UIs.text15Bold),
|
|
||||||
UIs.height7,
|
|
||||||
KvRow(k: 'CPU', v: '${srv.status.cpu.usedPercent()}%'),
|
|
||||||
KvRow(k: 'Mem', v: mem),
|
|
||||||
KvRow(k: 'Disk', v: disk),
|
|
||||||
KvRow(k: 'Net', v: net)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<void> afterFirstLayout(BuildContext context) async {
|
|
||||||
if (Stores.setting.autoCheckAppUpdate.fetch()) {
|
|
||||||
AppUpdateIface.doUpdate(
|
|
||||||
build: BuildData.build,
|
|
||||||
url: Urls.updateCfg,
|
|
||||||
context: context,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await Pros.server.load();
|
|
||||||
await Pros.server.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ 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/server_private_info.dart';
|
||||||
|
|
||||||
class IPerfPage extends StatefulWidget {
|
class IPerfPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
const IPerfPage({super.key, required this.spi});
|
const IPerfPage({super.key, required this.spi});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
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/res/provider.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
|
||||||
import 'package:server_box/data/model/server/ping_result.dart';
|
import 'package:server_box/data/model/server/ping_result.dart';
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ class _PingPageState extends State<PingPage>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Pros.server.serverOrder.isEmpty) {
|
if (ServerProvider.serverOrder.value.isEmpty) {
|
||||||
context.showSnackBar(l10n.pingNoServer);
|
context.showSnackBar(l10n.pingNoServer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,8 @@ class _PingPageState extends State<PingPage>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(Pros.server.servers.map((e) async {
|
await Future.wait(ServerProvider.servers.values.map((v) async {
|
||||||
|
final e = v.value;
|
||||||
if (e.client == null) {
|
if (e.client == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
import 'package:server_box/data/provider/private_key.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
|
|
||||||
import 'package:server_box/core/utils/server.dart';
|
import 'package:server_box/core/utils/server.dart';
|
||||||
import 'package:server_box/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
@@ -89,7 +89,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
|
|||||||
)),
|
)),
|
||||||
actions: Btn.ok(
|
actions: Btn.ok(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Pros.key.delete(widget.pki!);
|
PrivateKeyProvider.delete(widget.pki!);
|
||||||
context.pop();
|
context.pop();
|
||||||
context.pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
@@ -204,9 +204,9 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
|
|||||||
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
|
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
|
||||||
final pki = PrivateKeyInfo(id: name, key: decrypted);
|
final pki = PrivateKeyInfo(id: name, key: decrypted);
|
||||||
if (widget.pki != null) {
|
if (widget.pki != null) {
|
||||||
Pros.key.update(widget.pki!, pki);
|
PrivateKeyProvider.update(widget.pki!, pki);
|
||||||
} else {
|
} else {
|
||||||
Pros.key.add(pki);
|
PrivateKeyProvider.add(pki);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
context.showSnackBar(e.toString());
|
context.showSnackBar(e.toString());
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.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';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
@@ -35,16 +34,16 @@ class _PrivateKeyListState extends State<PrivateKeysListPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
return Consumer<PrivateKeyProvider>(
|
return PrivateKeyProvider.pkis.listenVal(
|
||||||
builder: (_, key, __) {
|
(pkis) {
|
||||||
if (key.pkis.isEmpty) {
|
if (pkis.isEmpty) {
|
||||||
return Center(child: Text(libL10n.empty));
|
return Center(child: Text(libL10n.empty));
|
||||||
}
|
}
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
padding: const EdgeInsets.all(13),
|
padding: const EdgeInsets.all(13),
|
||||||
itemCount: key.pkis.length,
|
itemCount: pkis.length,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
final item = key.pkis[idx];
|
final item = pkis[idx];
|
||||||
return CardX(
|
return CardX(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Text(
|
leading: Text(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import 'package:server_box/data/model/server/server_private_info.dart';
|
|||||||
import 'package:server_box/view/widget/two_line_text.dart';
|
import 'package:server_box/view/widget/two_line_text.dart';
|
||||||
|
|
||||||
class ProcessPage extends StatefulWidget {
|
class ProcessPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
const ProcessPage({super.key, required this.spi});
|
const ProcessPage({super.key, required this.spi});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -37,7 +37,7 @@ class _ProcessPageState extends State<ProcessPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_client = widget.spi.server?.client;
|
_client = widget.spi.server?.value.client;
|
||||||
final duration =
|
final duration =
|
||||||
Duration(seconds: Stores.setting.serverStatusUpdateInterval.fetch());
|
Duration(seconds: Stores.setting.serverStatusUpdateInterval.fetch());
|
||||||
_timer = Timer.periodic(duration, (_) => _refresh());
|
_timer = Timer.periodic(duration, (_) => _refresh());
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import 'package:server_box/view/widget/percent_circle.dart';
|
|||||||
import 'package:server_box/view/widget/two_line_text.dart';
|
import 'package:server_box/view/widget/two_line_text.dart';
|
||||||
|
|
||||||
final class PvePage extends StatefulWidget {
|
final class PvePage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
|
|
||||||
const PvePage({
|
const PvePage({
|
||||||
super.key,
|
super.key,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:provider/provider.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/app/server_detail_card.dart';
|
import 'package:server_box/data/model/app/server_detail_card.dart';
|
||||||
import 'package:server_box/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
@@ -22,14 +21,13 @@ import 'package:server_box/view/widget/server_func_btns.dart';
|
|||||||
|
|
||||||
import 'package:server_box/core/route.dart';
|
import 'package:server_box/core/route.dart';
|
||||||
import 'package:server_box/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
|
||||||
|
|
||||||
part 'misc.dart';
|
part 'misc.dart';
|
||||||
|
|
||||||
class ServerDetailPage extends StatefulWidget {
|
class ServerDetailPage extends StatefulWidget {
|
||||||
const ServerDetailPage({super.key, required this.spi});
|
const ServerDetailPage({super.key, required this.spi});
|
||||||
|
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ServerDetailPage> createState() => _ServerDetailPageState();
|
State<ServerDetailPage> createState() => _ServerDetailPageState();
|
||||||
@@ -79,7 +77,6 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<ServerProvider>(builder: (_, provider, __) {
|
|
||||||
final s = widget.spi.server;
|
final s = widget.spi.server;
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -87,8 +84,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
body: Center(child: Text(libL10n.empty)),
|
body: Center(child: Text(libL10n.empty)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _buildMainPage(s);
|
return s.listenVal(_buildMainPage);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMainPage(Server si) {
|
Widget _buildMainPage(Server si) {
|
||||||
@@ -96,12 +92,12 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
final logo = _buildLogo(si);
|
final logo = _buildLogo(si);
|
||||||
final children = [
|
final children = [
|
||||||
logo,
|
logo,
|
||||||
if (buildFuncs) ServerFuncBtns(spi: widget.spi),
|
if (buildFuncs) ServerFuncBtns(spi: si.spi),
|
||||||
];
|
];
|
||||||
for (final card in _cardsOrder) {
|
for (final card in _cardsOrder) {
|
||||||
final buildFunc = _cardBuildMap[card];
|
final buildFunc = _cardBuildMap[card];
|
||||||
if (buildFunc != null) {
|
if (buildFunc != null) {
|
||||||
children.add(buildFunc(si.status));
|
children.add(buildFunc(si));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -122,8 +118,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
title: Text(si.spi.name),
|
title: Text(si.spi.name),
|
||||||
actions: [
|
actions: [
|
||||||
ShareBtn(
|
ShareBtn(
|
||||||
data: widget.spi.toJsonString(),
|
data: si.spi.toJsonString(),
|
||||||
tip: widget.spi.name,
|
tip: si.spi.name,
|
||||||
tip2: '${libL10n.share} ${l10n.server} ~ ServerBox',
|
tip2: '${libL10n.share} ${l10n.server} ~ ServerBox',
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -160,7 +156,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAbout(ServerStatus ss) {
|
Widget _buildAbout(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
return CardX(
|
return CardX(
|
||||||
child: ExpandTile(
|
child: ExpandTile(
|
||||||
leading: const Icon(MingCute.information_fill, size: 20),
|
leading: const Icon(MingCute.information_fill, size: 20),
|
||||||
@@ -188,7 +185,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCPUView(ServerStatus ss) {
|
Widget _buildCPUView(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
final percent = ss.cpu.usedPercent(coreIdx: 0).toInt();
|
final percent = ss.cpu.usedPercent(coreIdx: 0).toInt();
|
||||||
final details = [
|
final details = [
|
||||||
_buildDetailPercent(ss.cpu.user, 'user'),
|
_buildDetailPercent(ss.cpu.user, 'user'),
|
||||||
@@ -352,7 +350,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMemView(ServerStatus ss) {
|
Widget _buildMemView(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
final free = ss.mem.free / ss.mem.total * 100;
|
final free = ss.mem.free / ss.mem.total * 100;
|
||||||
final avail = ss.mem.availPercent * 100;
|
final avail = ss.mem.availPercent * 100;
|
||||||
final used = ss.mem.usedPercent * 100;
|
final used = ss.mem.usedPercent * 100;
|
||||||
@@ -399,7 +398,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSwapView(ServerStatus ss) {
|
Widget _buildSwapView(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
if (ss.swap.total == 0) return UIs.placeholder;
|
if (ss.swap.total == 0) return UIs.placeholder;
|
||||||
final used = ss.swap.usedPercent * 100;
|
final used = ss.swap.usedPercent * 100;
|
||||||
final cached = ss.swap.cached / ss.swap.total * 100;
|
final cached = ss.swap.cached / ss.swap.total * 100;
|
||||||
@@ -434,7 +434,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGpuView(ServerStatus ss) {
|
Widget _buildGpuView(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
if (ss.nvidia == null || ss.nvidia?.isEmpty == true) return UIs.placeholder;
|
if (ss.nvidia == null || ss.nvidia?.isEmpty == true) return UIs.placeholder;
|
||||||
final children = ss.nvidia?.map((e) => _buildGpuItem(e)).toList() ?? [];
|
final children = ss.nvidia?.map((e) => _buildGpuItem(e)).toList() ?? [];
|
||||||
return CardX(
|
return CardX(
|
||||||
@@ -544,7 +545,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDiskView(ServerStatus ss) {
|
Widget _buildDiskView(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
final children = List.generate(
|
final children = List.generate(
|
||||||
ss.disk.length, (idx) => _buildDiskItem(ss.disk[idx], ss));
|
ss.disk.length, (idx) => _buildDiskItem(ss.disk[idx], ss));
|
||||||
return CardX(
|
return CardX(
|
||||||
@@ -608,7 +610,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildNetView(ServerStatus ss) {
|
Widget _buildNetView(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
final ns = ss.netSpeed;
|
final ns = ss.netSpeed;
|
||||||
final children = <Widget>[];
|
final children = <Widget>[];
|
||||||
final devices = ns.devices;
|
final devices = ns.devices;
|
||||||
@@ -691,14 +694,15 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTemperature(ServerStatus ss) {
|
Widget _buildTemperature(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
if (ss.temps.isEmpty) {
|
if (ss.temps.isEmpty) {
|
||||||
return UIs.placeholder;
|
return UIs.placeholder;
|
||||||
}
|
}
|
||||||
return CardX(
|
return CardX(
|
||||||
child: ExpandTile(
|
child: ExpandTile(
|
||||||
title: Text(l10n.temperature),
|
title: Text(l10n.temperature),
|
||||||
leading: const Icon(Icons.ac_unit, size: 17),
|
leading: const Icon(Icons.ac_unit, size: 20),
|
||||||
initiallyExpanded: _getInitExpand(ss.temps.devices.length),
|
initiallyExpanded: _getInitExpand(ss.temps.devices.length),
|
||||||
childrenPadding: const EdgeInsets.only(bottom: 7),
|
childrenPadding: const EdgeInsets.only(bottom: 7),
|
||||||
children: ss.temps.devices
|
children: ss.temps.devices
|
||||||
@@ -726,7 +730,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBatteries(ServerStatus ss) {
|
Widget _buildBatteries(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
if (ss.batteries.isEmpty) {
|
if (ss.batteries.isEmpty) {
|
||||||
return UIs.placeholder;
|
return UIs.placeholder;
|
||||||
}
|
}
|
||||||
@@ -767,7 +772,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSensors(ServerStatus ss) {
|
Widget _buildSensors(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
if (ss.sensors.isEmpty) return UIs.placeholder;
|
if (ss.sensors.isEmpty) return UIs.placeholder;
|
||||||
return CardX(
|
return CardX(
|
||||||
child: ExpandTile(
|
child: ExpandTile(
|
||||||
@@ -830,20 +836,21 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPve(_) {
|
Widget _buildPve(Server si) {
|
||||||
final addr = widget.spi.custom?.pveAddr;
|
final addr = si.spi.custom?.pveAddr;
|
||||||
if (addr == null) return UIs.placeholder;
|
if (addr == null || addr.isEmpty) return UIs.placeholder;
|
||||||
return CardX(
|
return CardX(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text('PVE'),
|
title: const Text('PVE'),
|
||||||
leading: const Icon(FontAwesome.server_solid, size: 17),
|
leading: const Icon(FontAwesome.server_solid, size: 17),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () => AppRoutes.pve(spi: widget.spi).go(context),
|
onTap: () => AppRoutes.pve(spi: si.spi).go(context),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCustom(ServerStatus ss) {
|
Widget _buildCustom(Server si) {
|
||||||
|
final ss = si.status;
|
||||||
if (ss.customCmds.isEmpty) return UIs.placeholder;
|
if (ss.customCmds.isEmpty) return UIs.placeholder;
|
||||||
return CardX(
|
return CardX(
|
||||||
child: ExpandTile(
|
child: ExpandTile(
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ import 'dart:convert';
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:provider/provider.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/custom.dart';
|
import 'package:server_box/data/model/server/custom.dart';
|
||||||
import 'package:server_box/data/model/server/wol_cfg.dart';
|
import 'package:server_box/data/model/server/wol_cfg.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
|
||||||
import 'package:server_box/core/route.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/server_private_info.dart';
|
||||||
@@ -16,7 +15,7 @@ import 'package:server_box/data/provider/private_key.dart';
|
|||||||
class ServerEditPage extends StatefulWidget {
|
class ServerEditPage extends StatefulWidget {
|
||||||
const ServerEditPage({super.key, this.spi});
|
const ServerEditPage({super.key, this.spi});
|
||||||
|
|
||||||
final ServerPrivateInfo? spi;
|
final Spi? spi;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ServerEditPage> createState() => _ServerEditPageState();
|
State<ServerEditPage> createState() => _ServerEditPageState();
|
||||||
@@ -148,7 +147,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
hint: 'root',
|
hint: 'root',
|
||||||
suggestion: false,
|
suggestion: false,
|
||||||
),
|
),
|
||||||
TagTile(tags: _tags, allTags: Pros.server.tags.value).cardx,
|
TagTile(tags: _tags, allTags: ServerProvider.tags.value).cardx,
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(l10n.autoConnect),
|
title: Text(l10n.autoConnect),
|
||||||
trailing: ListenableBuilder(
|
trailing: ListenableBuilder(
|
||||||
@@ -219,10 +218,10 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
|
|
||||||
Widget _buildKeyAuth() {
|
Widget _buildKeyAuth() {
|
||||||
const padding = EdgeInsets.only(left: 23, right: 13);
|
const padding = EdgeInsets.only(left: 23, right: 13);
|
||||||
return Consumer<PrivateKeyProvider>(
|
return PrivateKeyProvider.pkis.listenVal(
|
||||||
builder: (_, key, __) {
|
(pkis) {
|
||||||
final tiles = List<Widget>.generate(key.pkis.length, (index) {
|
final tiles = List<Widget>.generate(pkis.length, (index) {
|
||||||
final e = key.pkis[index];
|
final e = pkis[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
contentPadding: padding,
|
contentPadding: padding,
|
||||||
leading: Text(
|
leading: Text(
|
||||||
@@ -289,7 +288,6 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
return ExpandTile(
|
return ExpandTile(
|
||||||
title: Text(l10n.more),
|
title: Text(l10n.more),
|
||||||
children: [
|
children: [
|
||||||
UIs.height7,
|
|
||||||
Input(
|
Input(
|
||||||
controller: _logoUrlCtrl,
|
controller: _logoUrlCtrl,
|
||||||
type: TextInputType.url,
|
type: TextInputType.url,
|
||||||
@@ -300,13 +298,9 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
),
|
),
|
||||||
_buildAltUrl(),
|
_buildAltUrl(),
|
||||||
_buildEnvs(),
|
_buildEnvs(),
|
||||||
UIs.height7,
|
|
||||||
..._buildPVEs(),
|
..._buildPVEs(),
|
||||||
UIs.height7,
|
|
||||||
..._buildCustomCmds(),
|
..._buildCustomCmds(),
|
||||||
UIs.height7,
|
CenterGreyTitle(l10n.temperature),
|
||||||
Text(l10n.temperature, style: UIs.text13Grey),
|
|
||||||
UIs.height7,
|
|
||||||
Input(
|
Input(
|
||||||
controller: _preferTempDevCtrl,
|
controller: _preferTempDevCtrl,
|
||||||
type: TextInputType.text,
|
type: TextInputType.text,
|
||||||
@@ -336,27 +330,15 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
List<Widget> _buildPVEs() {
|
List<Widget> _buildPVEs() {
|
||||||
const addr = 'https://127.0.0.1:8006';
|
const addr = 'https://127.0.0.1:8006';
|
||||||
return [
|
return [
|
||||||
const Text('PVE', style: UIs.text13Grey),
|
const CenterGreyTitle('PVE'),
|
||||||
UIs.height7,
|
Input(
|
||||||
Autocomplete<String>(
|
controller: _pveAddrCtrl,
|
||||||
optionsBuilder: (val) {
|
|
||||||
final v = val.text;
|
|
||||||
if (v.startsWith(addr.substring(0, v.length))) {
|
|
||||||
return [addr];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
onSelected: (val) => _pveAddrCtrl.text = val,
|
|
||||||
fieldViewBuilder: (_, ctrl, node, __) => Input(
|
|
||||||
controller: ctrl,
|
|
||||||
type: TextInputType.url,
|
type: TextInputType.url,
|
||||||
icon: MingCute.web_line,
|
icon: MingCute.web_line,
|
||||||
node: node,
|
|
||||||
label: 'URL',
|
label: 'URL',
|
||||||
hint: addr,
|
hint: addr,
|
||||||
suggestion: false,
|
suggestion: false,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(MingCute.certificate_line),
|
leading: const Icon(MingCute.certificate_line),
|
||||||
title: Text('PVE ${l10n.ignoreCert}'),
|
title: Text('PVE ${l10n.ignoreCert}'),
|
||||||
@@ -376,8 +358,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
|
|
||||||
List<Widget> _buildCustomCmds() {
|
List<Widget> _buildCustomCmds() {
|
||||||
return [
|
return [
|
||||||
Text(l10n.customCmd, style: UIs.text13Grey),
|
CenterGreyTitle(l10n.customCmd),
|
||||||
UIs.height7,
|
|
||||||
_customCmds.listenVal(
|
_customCmds.listenVal(
|
||||||
(vals) {
|
(vals) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
@@ -455,9 +436,10 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: _jumpServer,
|
listenable: _jumpServer,
|
||||||
builder: (_, __) {
|
builder: (_, __) {
|
||||||
final children = Pros.server.servers
|
final children = ServerProvider.servers.values
|
||||||
.where((element) => element.spi.jumpId == null)
|
.map((e) => e.value)
|
||||||
.where((element) => element.spi.id != widget.spi?.id)
|
.where((e) => e.spi.jumpId == null)
|
||||||
|
.where((e) => e.spi.id != widget.spi?.id)
|
||||||
.map(
|
.map(
|
||||||
(e) => ListTile(
|
(e) => ListTile(
|
||||||
title: Text(e.spi.name),
|
title: Text(e.spi.name),
|
||||||
@@ -552,7 +534,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final spi = ServerPrivateInfo(
|
final spi = Spi(
|
||||||
name: _nameController.text.isEmpty
|
name: _nameController.text.isEmpty
|
||||||
? _ipController.text
|
? _ipController.text
|
||||||
: _nameController.text,
|
: _nameController.text,
|
||||||
@@ -561,7 +543,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
user: _usernameController.text,
|
user: _usernameController.text,
|
||||||
pwd: _passwordController.text.selfIfNotNullEmpty,
|
pwd: _passwordController.text.selfIfNotNullEmpty,
|
||||||
keyId: _keyIdx.value != null
|
keyId: _keyIdx.value != null
|
||||||
? Pros.key.pkis.elementAt(_keyIdx.value!).id
|
? PrivateKeyProvider.pkis.value.elementAt(_keyIdx.value!).id
|
||||||
: null,
|
: null,
|
||||||
tags: _tags.value.isEmpty ? null : _tags.value.toList(),
|
tags: _tags.value.isEmpty ? null : _tags.value.toList(),
|
||||||
alterUrl: _altUrlController.text.selfIfNotNullEmpty,
|
alterUrl: _altUrlController.text.selfIfNotNullEmpty,
|
||||||
@@ -573,9 +555,9 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (widget.spi == null) {
|
if (widget.spi == null) {
|
||||||
Pros.server.addServer(spi);
|
ServerProvider.addServer(spi);
|
||||||
} else {
|
} else {
|
||||||
Pros.server.updateServer(widget.spi!, spi);
|
ServerProvider.updateServer(widget.spi!, spi);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.pop();
|
context.pop();
|
||||||
@@ -589,7 +571,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initWithSpi(ServerPrivateInfo spi) {
|
void _initWithSpi(Spi spi) {
|
||||||
_nameController.text = spi.name;
|
_nameController.text = spi.name;
|
||||||
_ipController.text = spi.ip;
|
_ipController.text = spi.ip;
|
||||||
_portController.text = spi.port.toString();
|
_portController.text = spi.port.toString();
|
||||||
@@ -597,7 +579,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
if (spi.keyId == null) {
|
if (spi.keyId == null) {
|
||||||
_passwordController.text = spi.pwd ?? '';
|
_passwordController.text = spi.pwd ?? '';
|
||||||
} else {
|
} else {
|
||||||
_keyIdx.value = Pros.key.pkis.indexWhere(
|
_keyIdx.value = PrivateKeyProvider.pkis.value.indexWhere(
|
||||||
(e) => e.id == widget.spi!.keyId,
|
(e) => e.id == widget.spi!.keyId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -656,7 +638,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
final code = codes?.firstOrNull?.rawValue;
|
final code = codes?.firstOrNull?.rawValue;
|
||||||
if (code == null) return;
|
if (code == null) return;
|
||||||
try {
|
try {
|
||||||
final spi = ServerPrivateInfo.fromJson(json.decode(code));
|
final spi = Spi.fromJson(json.decode(code));
|
||||||
_initWithSpi(spi);
|
_initWithSpi(spi);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
context.showErrDialog(e, s);
|
context.showErrDialog(e, s);
|
||||||
@@ -680,7 +662,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
actions: Btn.ok(
|
actions: Btn.ok(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
context.pop();
|
context.pop();
|
||||||
Pros.server.delServer(widget.spi!.id);
|
ServerProvider.delServer(widget.spi!.id);
|
||||||
context.pop(true);
|
context.pop(true);
|
||||||
},
|
},
|
||||||
red: true,
|
red: true,
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import 'dart:math' as math;
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/core/extension/ssh_client.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/app/shell_func.dart';
|
||||||
import 'package:server_box/data/model/server/try_limiter.dart';
|
import 'package:server_box/data/model/server/try_limiter.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/widget/percent_circle.dart';
|
import 'package:server_box/view/widget/percent_circle.dart';
|
||||||
|
|
||||||
@@ -86,7 +84,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
|
|
||||||
Widget _buildPortrait() {
|
Widget _buildPortrait() {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: _buildTagsSwitcher(Pros.server),
|
appBar: _buildTagsSwitcher(),
|
||||||
body: GestureDetector(
|
body: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () => _autoHideKey.currentState?.show(),
|
onTap: () => _autoHideKey.currentState?.show(),
|
||||||
@@ -138,20 +136,21 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLandscapeBody() {
|
Widget _buildLandscapeBody() {
|
||||||
return Consumer<ServerProvider>(builder: (_, pro, __) {
|
return ServerProvider.serverOrder.listenVal((order) {
|
||||||
if (pro.serverOrder.isEmpty) {
|
if (order.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(libL10n.empty, textAlign: TextAlign.center),
|
child: Text(libL10n.empty, textAlign: TextAlign.center),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PageView.builder(
|
return PageView.builder(
|
||||||
itemCount: pro.serverOrder.length,
|
itemCount: order.length,
|
||||||
itemBuilder: (_, idx) {
|
itemBuilder: (_, idx) {
|
||||||
final id = pro.serverOrder[idx];
|
final id = order[idx];
|
||||||
final srv = pro.pick(id: id);
|
final srv = ServerProvider.pick(id: id);
|
||||||
if (srv == null) return UIs.placeholder;
|
if (srv == null) return UIs.placeholder;
|
||||||
|
|
||||||
|
return srv.listenVal((srv) {
|
||||||
final title = _buildServerCardTitle(srv);
|
final title = _buildServerCardTitle(srv);
|
||||||
final List<Widget> children = [
|
final List<Widget> children = [
|
||||||
title,
|
title,
|
||||||
@@ -174,38 +173,37 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
final child = Consumer<ServerProvider>(
|
return ServerProvider.serverOrder.listenVal(
|
||||||
builder: (_, pro, __) {
|
(order) {
|
||||||
if (!pro.tags.value.contains(_tag)) {
|
if (!ServerProvider.tags.value.contains(_tag)) {
|
||||||
_tag = null;
|
_tag = null;
|
||||||
}
|
}
|
||||||
if (pro.serverOrder.isEmpty) {
|
if (order.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(libL10n.empty, textAlign: TextAlign.center),
|
child: Text(libL10n.empty, textAlign: TextAlign.center),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final filtered = _filterServers(pro);
|
final filtered = _filterServers(order);
|
||||||
if (_useDoubleColumn &&
|
if (_useDoubleColumn &&
|
||||||
Stores.setting.doubleColumnServersPage.fetch()) {
|
Stores.setting.doubleColumnServersPage.fetch()) {
|
||||||
return _buildBodyMedium(pro: pro, filtered: filtered);
|
return _buildBodyMedium(filtered);
|
||||||
}
|
}
|
||||||
return _buildBodySmall(pro: pro, filtered: filtered);
|
return _buildBodySmall(filtered: filtered);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return child;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TagSwitcher _buildTagsSwitcher(ServerProvider provider) {
|
TagSwitcher _buildTagsSwitcher() {
|
||||||
return TagSwitcher(
|
return TagSwitcher(
|
||||||
tags: provider.tags,
|
tags: ServerProvider.tags,
|
||||||
width: _media.size.width,
|
width: _media.size.width,
|
||||||
onTagChanged: (p0) => setState(() {
|
onTagChanged: (p0) => setState(() {
|
||||||
_tag = p0;
|
_tag = p0;
|
||||||
@@ -215,7 +213,6 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBodySmall({
|
Widget _buildBodySmall({
|
||||||
required ServerProvider pro,
|
|
||||||
required List<String> filtered,
|
required List<String> filtered,
|
||||||
EdgeInsets? padding = const EdgeInsets.fromLTRB(7, 0, 7, 7),
|
EdgeInsets? padding = const EdgeInsets.fromLTRB(7, 0, 7, 7),
|
||||||
}) {
|
}) {
|
||||||
@@ -227,15 +224,14 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
// Issue #130
|
// Issue #130
|
||||||
if (index == count - 1) return UIs.height77;
|
if (index == count - 1) return UIs.height77;
|
||||||
return _buildEachServerCard(pro.pick(id: filtered[index]));
|
final vnode = ServerProvider.pick(id: filtered[index]);
|
||||||
|
if (vnode == null) return UIs.placeholder;
|
||||||
|
return vnode.listenVal(_buildEachServerCard);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBodyMedium({
|
Widget _buildBodyMedium(List<String> filtered) {
|
||||||
required ServerProvider pro,
|
|
||||||
required List<String> filtered,
|
|
||||||
}) {
|
|
||||||
final mid = (filtered.length / 2).ceil();
|
final mid = (filtered.length / 2).ceil();
|
||||||
final filteredLeft = filtered.sublist(0, mid);
|
final filteredLeft = filtered.sublist(0, mid);
|
||||||
final filteredRight = filtered.sublist(mid);
|
final filteredRight = filtered.sublist(mid);
|
||||||
@@ -243,14 +239,12 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildBodySmall(
|
child: _buildBodySmall(
|
||||||
pro: pro,
|
|
||||||
filtered: filteredLeft,
|
filtered: filteredLeft,
|
||||||
padding: const EdgeInsets.only(left: 7),
|
padding: const EdgeInsets.only(left: 7),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildBodySmall(
|
child: _buildBodySmall(
|
||||||
pro: pro,
|
|
||||||
filtered: filteredRight,
|
filtered: filteredRight,
|
||||||
padding: const EdgeInsets.only(right: 7),
|
padding: const EdgeInsets.only(right: 7),
|
||||||
),
|
),
|
||||||
@@ -416,7 +410,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildNormalCard(ServerStatus ss, ServerPrivateInfo spi) {
|
List<Widget> _buildNormalCard(ServerStatus ss, Spi spi) {
|
||||||
return [
|
return [
|
||||||
UIs.height13,
|
UIs.height13,
|
||||||
Row(
|
Row(
|
||||||
@@ -491,7 +485,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
),
|
),
|
||||||
() {
|
() {
|
||||||
TryLimiter.reset(s.spi.id);
|
TryLimiter.reset(s.spi.id);
|
||||||
Pros.server.refresh(spi: s.spi);
|
ServerProvider.refresh(spi: s.spi);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ServerConn.disconnected => (
|
ServerConn.disconnected => (
|
||||||
@@ -500,7 +494,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
size: 19,
|
size: 19,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
() => Pros.server.refresh(spi: s.spi)
|
() => ServerProvider.refresh(spi: s.spi)
|
||||||
),
|
),
|
||||||
ServerConn.finished => (
|
ServerConn.finished => (
|
||||||
const Icon(
|
const Icon(
|
||||||
@@ -508,7 +502,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
size: 17,
|
size: 17,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
() => Pros.server.closeServer(id: s.spi.id),
|
() => ServerProvider.closeServer(id: s.spi.id),
|
||||||
),
|
),
|
||||||
_ when Stores.setting.serverTabUseOldUI.fetch() => (
|
_ when Stores.setting.serverTabUseOldUI.fetch() => (
|
||||||
ServerFuncBtnsTopRight(spi: s.spi),
|
ServerFuncBtnsTopRight(spi: s.spi),
|
||||||
@@ -653,14 +647,14 @@ ${ss.err?.message ?? 'null'}
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> afterFirstLayout(BuildContext context) async {
|
Future<void> afterFirstLayout(BuildContext context) async {
|
||||||
await Pros.server.load();
|
ServerProvider.refresh();
|
||||||
Pros.server.startAutoRefresh();
|
ServerProvider.startAutoRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> _filterServers(ServerProvider pro) => pro.serverOrder
|
List<String> _filterServers(List<String> order) => order
|
||||||
.where((e) => pro.serverOrder.contains(e))
|
|
||||||
.where((e) =>
|
.where((e) =>
|
||||||
_tag == null || (pro.pick(id: e)?.spi.tags?.contains(_tag) ?? false))
|
_tag == null ||
|
||||||
|
(ServerProvider.pick(id: e)?.value.spi.tags?.contains(_tag) ?? false))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
static const _kCardHeightMin = 23.0;
|
static const _kCardHeightMin = 23.0;
|
||||||
@@ -747,7 +741,7 @@ class _CardStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension _ServerX on Server {
|
extension _ServerX on Server {
|
||||||
String? getTopRightStr(ServerPrivateInfo spi) {
|
String? getTopRightStr(Spi spi) {
|
||||||
switch (conn) {
|
switch (conn) {
|
||||||
case ServerConn.disconnected:
|
case ServerConn.disconnected:
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'dart:ui';
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
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/res/provider.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class ServerOrderPage extends StatefulWidget {
|
class ServerOrderPage extends StatefulWidget {
|
||||||
@@ -45,24 +45,28 @@ class _ServerOrderPageState extends State<ServerOrderPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
if (Pros.server.serverOrder.isEmpty) {
|
final orderNode = ServerProvider.serverOrder;
|
||||||
|
return orderNode.listenVal((order) {
|
||||||
|
if (order.isEmpty) {
|
||||||
return Center(child: Text(libL10n.empty));
|
return Center(child: Text(libL10n.empty));
|
||||||
}
|
}
|
||||||
return ReorderableListView.builder(
|
return ReorderableListView.builder(
|
||||||
footer: const SizedBox(height: 77),
|
footer: const SizedBox(height: 77),
|
||||||
onReorder: (oldIndex, newIndex) => setState(() {
|
onReorder: (oldIndex, newIndex) => setState(() {
|
||||||
Pros.server.serverOrder.move(
|
orderNode.value.move(
|
||||||
oldIndex,
|
oldIndex,
|
||||||
newIndex,
|
newIndex,
|
||||||
property: Stores.setting.serverOrder,
|
property: Stores.setting.serverOrder,
|
||||||
);
|
);
|
||||||
|
orderNode.notify();
|
||||||
}),
|
}),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||||
buildDefaultDragHandles: false,
|
buildDefaultDragHandles: false,
|
||||||
itemBuilder: (_, idx) => _buildItem(idx),
|
itemBuilder: (_, idx) => _buildItem(idx),
|
||||||
itemCount: Pros.server.serverOrder.length,
|
itemCount: order.length,
|
||||||
proxyDecorator: _proxyDecorator,
|
proxyDecorator: _proxyDecorator,
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildItem(int index) {
|
Widget _buildItem(int index) {
|
||||||
@@ -74,8 +78,8 @@ class _ServerOrderPageState extends State<ServerOrderPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCardTile(int index) {
|
Widget _buildCardTile(int index) {
|
||||||
final id = Pros.server.serverOrder[index];
|
final id = ServerProvider.serverOrder.value[index];
|
||||||
final spi = Pros.server.pick(id: id)?.spi;
|
final spi = ServerProvider.pick(id: id)?.value.spi;
|
||||||
if (spi == null) {
|
if (spi == null) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.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/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
|
|
||||||
class SnippetEditPage extends StatefulWidget {
|
class SnippetEditPage extends StatefulWidget {
|
||||||
const SnippetEditPage({super.key, this.snippet});
|
const SnippetEditPage({super.key, this.snippet});
|
||||||
@@ -55,7 +56,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
)),
|
)),
|
||||||
actions: Btn.ok(
|
actions: Btn.ok(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Pros.snippet.del(widget.snippet!);
|
SnippetProvider.del(widget.snippet!);
|
||||||
context.pop();
|
context.pop();
|
||||||
context.pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
@@ -89,9 +90,9 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
autoRunOn: _autoRunOn.value.isEmpty ? null : _autoRunOn.value,
|
autoRunOn: _autoRunOn.value.isEmpty ? null : _autoRunOn.value,
|
||||||
);
|
);
|
||||||
if (widget.snippet != null) {
|
if (widget.snippet != null) {
|
||||||
Pros.snippet.update(widget.snippet!, snippet);
|
SnippetProvider.update(widget.snippet!, snippet);
|
||||||
} else {
|
} else {
|
||||||
Pros.snippet.add(snippet);
|
SnippetProvider.add(snippet);
|
||||||
}
|
}
|
||||||
context.pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
@@ -120,7 +121,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
icon: Icons.note,
|
icon: Icons.note,
|
||||||
suggestion: true,
|
suggestion: true,
|
||||||
),
|
),
|
||||||
TagTile(tags: _tags, allTags: Pros.snippet.tags.value).cardx,
|
TagTile(tags: _tags, allTags: SnippetProvider.tags.value).cardx,
|
||||||
Input(
|
Input(
|
||||||
controller: _scriptController,
|
controller: _scriptController,
|
||||||
node: _scriptNode,
|
node: _scriptNode,
|
||||||
@@ -145,7 +146,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
final subtitle = vals.isEmpty
|
final subtitle = vals.isEmpty
|
||||||
? null
|
? null
|
||||||
: vals
|
: vals
|
||||||
.map((e) => Pros.server.pick(id: e)?.spi.name ?? e)
|
.map((e) => ServerProvider.pick(id: e)?.value.spi.name ?? e)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Padding(
|
leading: const Padding(
|
||||||
@@ -163,11 +164,12 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
vals.removeWhere((e) => !Pros.server.serverOrder.contains(e));
|
vals.removeWhere(
|
||||||
|
(e) => !ServerProvider.serverOrder.value.contains(e));
|
||||||
final serverIds = await context.showPickDialog(
|
final serverIds = await context.showPickDialog(
|
||||||
title: l10n.autoRun,
|
title: l10n.autoRun,
|
||||||
items: Pros.server.serverOrder,
|
items: ServerProvider.serverOrder.value,
|
||||||
name: (e) => Pros.server.pick(id: e)?.spi.name ?? e,
|
name: (e) => ServerProvider.pick(id: e)?.value.spi.name ?? e,
|
||||||
initial: vals,
|
initial: vals,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
@@ -38,13 +37,13 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
return Consumer<SnippetProvider>(
|
return SnippetProvider.snippets.listenVal(
|
||||||
builder: (_, provider, __) {
|
(snippets) {
|
||||||
if (provider.snippets.isEmpty) {
|
if (snippets.isEmpty) {
|
||||||
return Center(child: Text(libL10n.empty));
|
return Center(child: Text(libL10n.empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
final filtered = provider.snippets
|
final filtered = snippets
|
||||||
.where((e) => _tag == null || (e.tags?.contains(_tag) ?? false))
|
.where((e) => _tag == null || (e.tags?.contains(_tag) ?? false))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 11),
|
padding: const EdgeInsets.symmetric(horizontal: 11),
|
||||||
itemCount: filtered.length,
|
itemCount: filtered.length,
|
||||||
onReorder: (oldIdx, newIdx) => setState(() {
|
onReorder: (oldIdx, newIdx) => setState(() {
|
||||||
provider.snippets.moveByItem(
|
snippets.moveByItem(
|
||||||
oldIdx,
|
oldIdx,
|
||||||
newIdx,
|
newIdx,
|
||||||
filtered: filtered,
|
filtered: filtered,
|
||||||
@@ -62,7 +61,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
header: TagSwitcher(
|
header: TagSwitcher(
|
||||||
tags: provider.tags,
|
tags: SnippetProvider.tags,
|
||||||
onTagChanged: (tag) => setState(() => _tag = tag),
|
onTagChanged: (tag) => setState(() => _tag = tag),
|
||||||
initTag: _tag,
|
initTag: _tag,
|
||||||
width: _media.size.width,
|
width: _media.size.width,
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import 'package:server_box/core/extension/context/locale.dart';
|
|||||||
import 'package:server_box/core/utils/ssh_auth.dart';
|
import 'package:server_box/core/utils/ssh_auth.dart';
|
||||||
import 'package:server_box/core/utils/server.dart';
|
import 'package:server_box/core/utils/server.dart';
|
||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
import 'package:server_box/data/provider/virtual_keyboard.dart';
|
import 'package:server_box/data/provider/virtual_keyboard.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:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
import 'package:xterm/core.dart';
|
import 'package:xterm/core.dart';
|
||||||
@@ -26,7 +26,7 @@ import 'package:server_box/data/res/terminal.dart';
|
|||||||
const _echoPWD = 'echo \$PWD';
|
const _echoPWD = 'echo \$PWD';
|
||||||
|
|
||||||
class SSHPage extends StatefulWidget {
|
class SSHPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
final String? initCmd;
|
final String? initCmd;
|
||||||
final Snippet? initSnippet;
|
final Snippet? initSnippet;
|
||||||
final bool notFromTab;
|
final bool notFromTab;
|
||||||
@@ -68,7 +68,7 @@ class SSHPageState extends State<SSHPage>
|
|||||||
|
|
||||||
bool _isDark = false;
|
bool _isDark = false;
|
||||||
Timer? _virtKeyLongPressTimer;
|
Timer? _virtKeyLongPressTimer;
|
||||||
late SSHClient? _client = widget.spi.server?.client;
|
late SSHClient? _client = widget.spi.server?.value.client;
|
||||||
Timer? _discontinuityTimer;
|
Timer? _discontinuityTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -298,10 +298,10 @@ class SSHPageState extends State<SSHPage>
|
|||||||
case VirtualKeyFunc.snippet:
|
case VirtualKeyFunc.snippet:
|
||||||
final snippets = await context.showPickWithTagDialog<Snippet>(
|
final snippets = await context.showPickWithTagDialog<Snippet>(
|
||||||
title: l10n.snippet,
|
title: l10n.snippet,
|
||||||
tags: Pros.snippet.tags,
|
tags: SnippetProvider.tags,
|
||||||
itemsBuilder: (e) {
|
itemsBuilder: (e) {
|
||||||
if (e == null) return Pros.snippet.snippets;
|
if (e == null) return SnippetProvider.snippets.value;
|
||||||
return Pros.snippet.snippets
|
return SnippetProvider.snippets.value
|
||||||
.where((element) => element.tags?.contains(e) ?? false)
|
.where((element) => element.tags?.contains(e) ?? false)
|
||||||
.toList();
|
.toList();
|
||||||
},
|
},
|
||||||
@@ -417,7 +417,7 @@ class SSHPageState extends State<SSHPage>
|
|||||||
|
|
||||||
_initService();
|
_initService();
|
||||||
|
|
||||||
for (final snippet in Pros.snippet.snippets) {
|
for (final snippet in SnippetProvider.snippets.value) {
|
||||||
if (snippet.autoRunOn?.contains(widget.spi.id) == true) {
|
if (snippet.autoRunOn?.contains(widget.spi.id) == true) {
|
||||||
snippet.runInTerm(_terminal, widget.spi);
|
snippet.runInTerm(_terminal, widget.spi);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:provider/provider.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/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/view/page/ssh/page.dart';
|
import 'package:server_box/view/page/ssh/page.dart';
|
||||||
|
|
||||||
class SSHTabPage extends StatefulWidget {
|
class SSHTabPage extends StatefulWidget {
|
||||||
@@ -86,8 +84,8 @@ class _SSHTabPageState extends State<SSHTabPage>
|
|||||||
Widget _buildAddPage() {
|
Widget _buildAddPage() {
|
||||||
return Center(
|
return Center(
|
||||||
key: const Key('sshTabAddServer'),
|
key: const Key('sshTabAddServer'),
|
||||||
child: Consumer<ServerProvider>(builder: (_, pro, __) {
|
child: ServerProvider.serverOrder.listenVal((order) {
|
||||||
if (pro.serverOrder.isEmpty) {
|
if (order.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(libL10n.empty, textAlign: TextAlign.center),
|
child: Text(libL10n.empty, textAlign: TextAlign.center),
|
||||||
);
|
);
|
||||||
@@ -98,7 +96,7 @@ class _SSHTabPageState extends State<SSHTabPage>
|
|||||||
padding: const EdgeInsets.all(7),
|
padding: const EdgeInsets.all(7),
|
||||||
cacheExtent: 50,
|
cacheExtent: 50,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
final spi = Pros.server.pick(id: pro.serverOrder[idx])?.spi;
|
final spi = ServerProvider.pick(id: order[idx])?.value.spi;
|
||||||
if (spi == null) return UIs.placeholder;
|
if (spi == null) return UIs.placeholder;
|
||||||
return CardX(
|
return CardX(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -117,7 +115,7 @@ class _SSHTabPageState extends State<SSHTabPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: pro.servers.length,
|
itemCount: ServerProvider.servers.length,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
childAspectRatio: 3 * (ratio / (9 / 16)),
|
childAspectRatio: 3 * (ratio / (9 / 16)),
|
||||||
@@ -147,7 +145,7 @@ class _SSHTabPageState extends State<SSHTabPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTapInitCard(ServerPrivateInfo spi) async {
|
void _onTapInitCard(Spi spi) async {
|
||||||
final name = () {
|
final name = () {
|
||||||
final reg = RegExp('${spi.name}\\((\\d+)\\)');
|
final reg = RegExp('${spi.name}\\((\\d+)\\)');
|
||||||
final idxs = _tabMap.keys
|
final idxs = _tabMap.keys
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ 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/model/sftp/worker.dart';
|
import 'package:server_box/data/model/sftp/worker.dart';
|
||||||
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/view/widget/omit_start_text.dart';
|
import 'package:server_box/view/widget/omit_start_text.dart';
|
||||||
|
|
||||||
import 'package:server_box/core/route.dart';
|
import 'package:server_box/core/route.dart';
|
||||||
@@ -282,10 +283,10 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
context.pop();
|
context.pop();
|
||||||
|
|
||||||
final spi = await context.showPickSingleDialog<ServerPrivateInfo>(
|
final spi = await context.showPickSingleDialog<Spi>(
|
||||||
title: libL10n.select,
|
title: libL10n.select,
|
||||||
items: Pros.server.serverOrder
|
items: ServerProvider.serverOrder.value
|
||||||
.map((e) => Pros.server.pick(id: e)?.spi)
|
.map((e) => ServerProvider.pick(id: e)?.value.spi)
|
||||||
.toList(),
|
.toList(),
|
||||||
name: (e) => e.name,
|
name: (e) => e.name,
|
||||||
);
|
);
|
||||||
@@ -299,7 +300,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pros.sftp.add(SftpReq(
|
SftpProvider.add(SftpReq(
|
||||||
spi,
|
spi,
|
||||||
'$remotePath/$fileName',
|
'$remotePath/$fileName',
|
||||||
file.absolute.path,
|
file.absolute.path,
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import 'package:server_box/core/utils/comparator.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/model/sftp/browser_status.dart';
|
import 'package:server_box/data/model/sftp/browser_status.dart';
|
||||||
import 'package:server_box/data/model/sftp/worker.dart';
|
import 'package:server_box/data/model/sftp/worker.dart';
|
||||||
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.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/widget/omit_start_text.dart';
|
import 'package:server_box/view/widget/omit_start_text.dart';
|
||||||
import 'package:server_box/view/widget/two_line_text.dart';
|
import 'package:server_box/view/widget/two_line_text.dart';
|
||||||
@@ -21,7 +21,7 @@ import 'package:server_box/view/widget/unix_perm.dart';
|
|||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
|
|
||||||
class SftpPage extends StatefulWidget {
|
class SftpPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
final String? initPath;
|
final String? initPath;
|
||||||
final bool isSelect;
|
final bool isSelect;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class SftpPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||||
late final _status = SftpBrowserStatus(_client);
|
late final _status = SftpBrowserStatus(_client);
|
||||||
late final _client = widget.spi.server!.client!;
|
late final _client = widget.spi.server!.value.client!;
|
||||||
final _sortOption = _SortOption().vn;
|
final _sortOption = _SortOption().vn;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -303,7 +303,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
|||||||
localPath,
|
localPath,
|
||||||
SftpReqType.download,
|
SftpReqType.download,
|
||||||
);
|
);
|
||||||
Pros.sftp.add(req, completer: completer);
|
SftpProvider.add(req, completer: completer);
|
||||||
final (suc, err) = await context.showLoadingDialog(
|
final (suc, err) = await context.showLoadingDialog(
|
||||||
fn: () => completer.future,
|
fn: () => completer.future,
|
||||||
);
|
);
|
||||||
@@ -311,7 +311,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
|||||||
|
|
||||||
final result = await AppRoutes.editor(path: localPath).go<bool>(context);
|
final result = await AppRoutes.editor(path: localPath).go<bool>(context);
|
||||||
if (result != null && result) {
|
if (result != null && result) {
|
||||||
Pros.sftp.add(SftpReq(
|
SftpProvider.add(SftpReq(
|
||||||
req.spi,
|
req.spi,
|
||||||
remotePath,
|
remotePath,
|
||||||
localPath,
|
localPath,
|
||||||
@@ -335,7 +335,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
|||||||
context.pop();
|
context.pop();
|
||||||
final remotePath = _getRemotePath(name);
|
final remotePath = _getRemotePath(name);
|
||||||
|
|
||||||
Pros.sftp.add(
|
SftpProvider.add(
|
||||||
SftpReq(
|
SftpReq(
|
||||||
widget.spi,
|
widget.spi,
|
||||||
remotePath,
|
remotePath,
|
||||||
@@ -663,8 +663,9 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
|||||||
delegate: SearchPage(
|
delegate: SearchPage(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||||
future: (q) => find(q).toList(),
|
future: (q) => find(q).toList(),
|
||||||
builder: (ctx, e) => _buildItem(e, beforeTap: () => ctx.pop()),
|
builder: (ctx, e) => _buildItem(e, beforeTap: ctx.pop),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
);
|
);
|
||||||
@@ -701,7 +702,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
|||||||
final fileName = path.split(Platform.pathSeparator).lastOrNull;
|
final fileName = path.split(Platform.pathSeparator).lastOrNull;
|
||||||
final remotePath = '$remoteDir/$fileName';
|
final remotePath = '$remoteDir/$fileName';
|
||||||
Loggers.app.info('SFTP upload local: $path, remote: $remotePath');
|
Loggers.app.info('SFTP upload local: $path, remote: $remotePath');
|
||||||
Pros.sftp.add(
|
SftpProvider.add(
|
||||||
SftpReq(widget.spi, remotePath, path, SftpReqType.upload),
|
SftpReq(widget.spi, remotePath, path, SftpReqType.upload),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.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/sftp/worker.dart';
|
import 'package:server_box/data/model/sftp/worker.dart';
|
||||||
import 'package:server_box/data/provider/sftp.dart';
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
|
|
||||||
class SftpMissionPage extends StatefulWidget {
|
class SftpMissionPage extends StatefulWidget {
|
||||||
const SftpMissionPage({super.key});
|
const SftpMissionPage({super.key});
|
||||||
@@ -26,16 +24,15 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
return Consumer<SftpProvider>(builder: (__, pro, _) {
|
return SftpProvider.status.listenVal((status) {
|
||||||
if (pro.status.isEmpty) {
|
if (status.isEmpty) {
|
||||||
return Center(child: Text(libL10n.empty));
|
return Center(child: Text(libL10n.empty));
|
||||||
}
|
}
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
padding: const EdgeInsets.all(11),
|
padding: const EdgeInsets.all(11),
|
||||||
itemCount: pro.status.length,
|
itemCount: status.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final status = pro.status[index];
|
return _buildItem(status[index]);
|
||||||
return _buildItem(status);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -165,7 +162,7 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
|
|||||||
)),
|
)),
|
||||||
actions: Btn.ok(
|
actions: Btn.ok(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Pros.sftp.cancel(id);
|
SftpProvider.cancel(id);
|
||||||
context.pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
).toList,
|
).toList,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:server_box/data/model/server/systemd.dart';
|
|||||||
import 'package:server_box/data/provider/systemd.dart';
|
import 'package:server_box/data/provider/systemd.dart';
|
||||||
|
|
||||||
final class SystemdPageArgs {
|
final class SystemdPageArgs {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
|
|
||||||
const SystemdPageArgs({
|
const SystemdPageArgs({
|
||||||
required this.spi,
|
required this.spi,
|
||||||
@@ -102,7 +102,7 @@ final class _SystemdPageState extends State<SystemdPage> {
|
|||||||
return PopupMenu(
|
return PopupMenu(
|
||||||
items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(),
|
items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(),
|
||||||
onSelected: (val) async {
|
onSelected: (val) async {
|
||||||
final cmd = unit.getCmd(func: val, isRoot: _pro.isRoot);
|
final cmd = unit.getCmd(func: val, isRoot: widget.args.spi.isRoot);
|
||||||
final sure = await context.showRoundDialog(
|
final sure = await context.showRoundDialog(
|
||||||
title: libL10n.attention,
|
title: libL10n.attention,
|
||||||
child: SimpleMarkdown(data: '```shell\n$cmd\n```'),
|
child: SimpleMarkdown(data: '```shell\n$cmd\n```'),
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import 'package:server_box/core/extension/context/locale.dart';
|
|||||||
import 'package:server_box/data/model/app/menu/base.dart';
|
import 'package:server_box/data/model/app/menu/base.dart';
|
||||||
import 'package:server_box/data/model/app/menu/server_func.dart';
|
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/provider/server.dart';
|
||||||
|
import 'package:server_box/data/provider/snippet.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 'package:server_box/view/page/systemd.dart';
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ import 'package:server_box/core/utils/server.dart';
|
|||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
|
||||||
class ServerFuncBtnsTopRight extends StatelessWidget {
|
class ServerFuncBtnsTopRight extends StatelessWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
|
|
||||||
const ServerFuncBtnsTopRight({
|
const ServerFuncBtnsTopRight({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -40,7 +41,7 @@ class ServerFuncBtns extends StatelessWidget {
|
|||||||
required this.spi,
|
required this.spi,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -90,7 +91,7 @@ class ServerFuncBtns extends StatelessWidget {
|
|||||||
|
|
||||||
void _onTapMoreBtns(
|
void _onTapMoreBtns(
|
||||||
ServerFuncBtn value,
|
ServerFuncBtn value,
|
||||||
ServerPrivateInfo spi,
|
Spi spi,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) async {
|
) async {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
@@ -104,16 +105,16 @@ void _onTapMoreBtns(
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case ServerFuncBtn.snippet:
|
case ServerFuncBtn.snippet:
|
||||||
if (Pros.snippet.snippets.isEmpty) {
|
if (SnippetProvider.snippets.value.isEmpty) {
|
||||||
context.showSnackBar(libL10n.empty);
|
context.showSnackBar(libL10n.empty);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final snippets = await context.showPickWithTagDialog<Snippet>(
|
final snippets = await context.showPickWithTagDialog<Snippet>(
|
||||||
title: l10n.snippet,
|
title: l10n.snippet,
|
||||||
tags: Pros.snippet.tags,
|
tags: SnippetProvider.tags,
|
||||||
itemsBuilder: (e) {
|
itemsBuilder: (e) {
|
||||||
if (e == null) return Pros.snippet.snippets;
|
if (e == null) return SnippetProvider.snippets.value;
|
||||||
return Pros.snippet.snippets
|
return SnippetProvider.snippets.value
|
||||||
.where((element) => element.tags?.contains(e) ?? false)
|
.where((element) => element.tags?.contains(e) ?? false)
|
||||||
.toList();
|
.toList();
|
||||||
},
|
},
|
||||||
@@ -172,7 +173,7 @@ void _onTapMoreBtns(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _gotoSSH(ServerPrivateInfo spi, BuildContext context) async {
|
void _gotoSSH(Spi spi, BuildContext context) async {
|
||||||
// run built-in ssh on macOS due to incompatibility
|
// run built-in ssh on macOS due to incompatibility
|
||||||
if (isMobile || isMacOS) {
|
if (isMobile || isMacOS) {
|
||||||
AppRoutes.ssh(spi: spi).go(context);
|
AppRoutes.ssh(spi: spi).go(context);
|
||||||
@@ -219,7 +220,7 @@ void _gotoSSH(ServerPrivateInfo spi, BuildContext context) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _checkClient(BuildContext context, String id) {
|
bool _checkClient(BuildContext context, String id) {
|
||||||
final server = Pros.server.pick(id: id);
|
final server = ServerProvider.pick(id: id)?.value;
|
||||||
if (server == null || server.client == null) {
|
if (server == null || server.client == null) {
|
||||||
context.showSnackBar(l10n.waitConnection);
|
context.showSnackBar(l10n.waitConnection);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
10
pubspec.lock
10
pubspec.lock
@@ -400,14 +400,6 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_adaptive_scaffold:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_adaptive_scaffold
|
|
||||||
sha256: a464b74540401cade07af0ae84d19f210534cac67651a150fb413507040b74f6
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.12"
|
|
||||||
flutter_background_service:
|
flutter_background_service:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1049,7 +1041,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "3.3.0"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
provider: ^6.0.0
|
|
||||||
hive_flutter: ^1.1.0
|
hive_flutter: ^1.1.0
|
||||||
dio: ^5.2.1
|
dio: ^5.2.1
|
||||||
easy_isolate: ^1.3.0
|
easy_isolate: ^1.3.0
|
||||||
@@ -27,7 +26,6 @@ dependencies:
|
|||||||
fl_chart: ^0.67.0
|
fl_chart: ^0.67.0
|
||||||
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
|
|
||||||
extended_image: ^8.2.1
|
extended_image: ^8.2.1
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
dartssh2:
|
dartssh2:
|
||||||
|
|||||||
Reference in New Issue
Block a user