mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-02-23 16:45:27 +01:00
fix: sftp dl
This commit is contained in:
@@ -12,7 +12,6 @@ class PersistentStore<E> {
|
||||
}
|
||||
|
||||
StoreProperty<T> property<T>(String key, {T? defaultValue}) {
|
||||
|
||||
return StoreProperty<T>(box, key, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,21 @@ enum GenSSHClientStatus {
|
||||
pwd,
|
||||
}
|
||||
|
||||
String getPrivateKey(String id) {
|
||||
final key = locator<PrivateKeyStore>().get(id);
|
||||
if (key == null) {
|
||||
throw SSHErr(
|
||||
type: SSHErrType.noPrivateKey,
|
||||
message: 'key [$id] not found',
|
||||
);
|
||||
}
|
||||
return key.privateKey;
|
||||
}
|
||||
|
||||
Future<SSHClient> genClient(
|
||||
ServerPrivateInfo spi, {
|
||||
void Function(GenSSHClientStatus)? onStatus,
|
||||
String? privateKey,
|
||||
}) async {
|
||||
onStatus?.call(GenSSHClientStatus.socket);
|
||||
late SSHSocket socket;
|
||||
@@ -66,17 +78,12 @@ Future<SSHClient> genClient(
|
||||
onPasswordRequest: () => spi.pwd,
|
||||
);
|
||||
}
|
||||
final key = locator<PrivateKeyStore>().get(spi.pubKeyId!);
|
||||
if (key == null) {
|
||||
throw SSHErr(
|
||||
type: SSHErrType.noPrivateKey,
|
||||
message: 'key [${spi.pubKeyId}] not found',
|
||||
);
|
||||
}
|
||||
privateKey ??= getPrivateKey(spi.pubKeyId!);
|
||||
|
||||
onStatus?.call(GenSSHClientStatus.key);
|
||||
return SSHClient(
|
||||
socket,
|
||||
username: spi.user,
|
||||
identities: await compute(loadIndentity, key.privateKey),
|
||||
identities: await compute(loadIndentity, privateKey),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../core/utils/server.dart';
|
||||
import '../server/server_private_info.dart';
|
||||
import 'worker.dart';
|
||||
|
||||
class SftpReqItem {
|
||||
class SftpReq {
|
||||
final ServerPrivateInfo spi;
|
||||
final String remotePath;
|
||||
final String localPath;
|
||||
final SftpReqType type;
|
||||
String? privateKey;
|
||||
|
||||
SftpReqItem(this.spi, this.remotePath, this.localPath);
|
||||
SftpReq(
|
||||
this.spi,
|
||||
this.remotePath,
|
||||
this.localPath,
|
||||
this.type,
|
||||
) {
|
||||
if (spi.pubKeyId != null) {
|
||||
privateKey = getPrivateKey(spi.pubKeyId!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SftpReqType { download, upload }
|
||||
|
||||
class SftpReq {
|
||||
final SftpReqItem item;
|
||||
final String? privateKey;
|
||||
final SftpReqType type;
|
||||
|
||||
SftpReq({required this.item, this.privateKey, required this.type});
|
||||
}
|
||||
|
||||
class SftpReqStatus {
|
||||
final int id;
|
||||
final SftpReqItem item;
|
||||
final SftpReq req;
|
||||
final void Function() notifyListeners;
|
||||
late SftpWorker worker;
|
||||
final Completer? completer;
|
||||
|
||||
String get fileName => item.localPath.split('/').last;
|
||||
String get fileName => req.localPath.split('/').last;
|
||||
|
||||
// status of the download
|
||||
double? progress;
|
||||
@@ -38,17 +42,14 @@ class SftpReqStatus {
|
||||
Duration? spentTime;
|
||||
|
||||
SftpReqStatus({
|
||||
required this.item,
|
||||
required this.req,
|
||||
required this.notifyListeners,
|
||||
required SftpReqType type,
|
||||
this.completer,
|
||||
}) : id = DateTime.now().microsecondsSinceEpoch {
|
||||
worker = SftpWorker(
|
||||
onNotify: onNotify,
|
||||
item: item,
|
||||
type: type,
|
||||
);
|
||||
worker.init();
|
||||
req: req,
|
||||
)..init();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -79,6 +80,7 @@ class SftpReqStatus {
|
||||
spentTime = event;
|
||||
break;
|
||||
default:
|
||||
error = Exception('unknown event: $event');
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -6,20 +6,19 @@ import 'dart:typed_data';
|
||||
import 'package:dartssh2/dartssh2.dart';
|
||||
import 'package:easy_isolate/easy_isolate.dart';
|
||||
import 'package:toolbox/core/utils/misc.dart';
|
||||
import 'package:toolbox/core/utils/server.dart';
|
||||
|
||||
import 'req.dart';
|
||||
|
||||
class SftpWorker {
|
||||
final Function(Object event) onNotify;
|
||||
final SftpReqItem item;
|
||||
final SftpReqType type;
|
||||
final SftpReq req;
|
||||
|
||||
final worker = Worker();
|
||||
|
||||
SftpWorker({
|
||||
required this.onNotify,
|
||||
required this.item,
|
||||
required this.type,
|
||||
required this.req,
|
||||
});
|
||||
|
||||
void dispose() {
|
||||
@@ -35,7 +34,7 @@ class SftpWorker {
|
||||
isolateMessageHandler,
|
||||
errorHandler: print,
|
||||
);
|
||||
worker.sendMessage(SftpReq(item: item, type: type));
|
||||
worker.sendMessage(req);
|
||||
}
|
||||
|
||||
/// Handle the messages coming from the isolate
|
||||
@@ -69,29 +68,19 @@ Future<void> isolateMessageHandler(
|
||||
}
|
||||
|
||||
Future<void> _download(
|
||||
SftpReq data,
|
||||
SftpReq req,
|
||||
SendPort mainSendPort,
|
||||
SendErrorFunction sendError,
|
||||
) async {
|
||||
try {
|
||||
mainSendPort.send(SftpWorkerStatus.preparing);
|
||||
final watch = Stopwatch()..start();
|
||||
final item = data.item;
|
||||
final spi = item.spi;
|
||||
final socket = await SSHSocket.connect(spi.ip, spi.port);
|
||||
SSHClient client;
|
||||
if (spi.pubKeyId == null) {
|
||||
client = SSHClient(socket,
|
||||
username: spi.user, onPasswordRequest: () => spi.pwd);
|
||||
} else {
|
||||
client = SSHClient(socket,
|
||||
username: spi.user, identities: SSHKeyPair.fromPem(data.privateKey!));
|
||||
}
|
||||
final client = await genClient(req.spi, privateKey: req.privateKey);
|
||||
mainSendPort.send(SftpWorkerStatus.sshConnectted);
|
||||
|
||||
final remotePath = item.remotePath;
|
||||
final localPath = item.localPath;
|
||||
await Directory(localPath.substring(0, item.localPath.lastIndexOf('/')))
|
||||
final remotePath = req.remotePath;
|
||||
final localPath = req.localPath;
|
||||
await Directory(localPath.substring(0, req.localPath.lastIndexOf('/')))
|
||||
.create(recursive: true);
|
||||
final local = File(localPath);
|
||||
if (await local.exists()) {
|
||||
@@ -104,7 +93,8 @@ Future<void> _download(
|
||||
mainSendPort.send(Exception('can not get file size'));
|
||||
return;
|
||||
}
|
||||
const defaultChunkSize = 1024 * 1024;
|
||||
// Read 10m each time
|
||||
const defaultChunkSize = 1024 * 1024 * 10;
|
||||
final chunkSize = size > defaultChunkSize ? defaultChunkSize : size;
|
||||
mainSendPort.send(size);
|
||||
mainSendPort.send(SftpWorkerStatus.downloading);
|
||||
@@ -125,29 +115,19 @@ Future<void> _download(
|
||||
}
|
||||
|
||||
Future<void> _upload(
|
||||
SftpReq data,
|
||||
SftpReq req,
|
||||
SendPort mainSendPort,
|
||||
SendErrorFunction sendError,
|
||||
) async {
|
||||
try {
|
||||
mainSendPort.send(SftpWorkerStatus.preparing);
|
||||
final watch = Stopwatch()..start();
|
||||
final item = data.item;
|
||||
final spi = item.spi;
|
||||
final socket = await SSHSocket.connect(spi.ip, spi.port);
|
||||
SSHClient client;
|
||||
if (spi.pubKeyId == null) {
|
||||
client = SSHClient(socket,
|
||||
username: spi.user, onPasswordRequest: () => spi.pwd);
|
||||
} else {
|
||||
client = SSHClient(socket,
|
||||
username: spi.user, identities: SSHKeyPair.fromPem(data.privateKey!));
|
||||
}
|
||||
final client = await genClient(req.spi, privateKey: req.privateKey);
|
||||
mainSendPort.send(SftpWorkerStatus.sshConnectted);
|
||||
|
||||
final localPath = item.localPath;
|
||||
final localPath = req.localPath;
|
||||
final remotePath =
|
||||
item.remotePath + (getFileName(localPath) ?? 'srvbox_sftp_upload');
|
||||
req.remotePath + (getFileName(localPath) ?? 'srvbox_sftp_upload');
|
||||
final local = File(localPath);
|
||||
if (!await local.exists()) {
|
||||
mainSendPort.send(Exception('local file not exists'));
|
||||
|
||||
@@ -14,7 +14,7 @@ class SftpProvider extends ProviderBase {
|
||||
found = _status.where((e) => e.id == id);
|
||||
}
|
||||
if (fileName != null) {
|
||||
found = found.where((e) => e.item.localPath.split('/').last == fileName);
|
||||
found = found.where((e) => e.req.localPath.split('/').last == fileName);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
@@ -25,12 +25,11 @@ class SftpProvider extends ProviderBase {
|
||||
return found.first;
|
||||
}
|
||||
|
||||
void add(SftpReqItem item, SftpReqType type, {Completer? completer}) {
|
||||
void add(SftpReq req, {Completer? completer}) {
|
||||
_status.add(SftpReqStatus(
|
||||
item: item,
|
||||
notifyListeners: notifyListeners,
|
||||
type: type,
|
||||
completer: completer,
|
||||
req: req,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 389;
|
||||
static const int build = 395;
|
||||
static const String engine = "3.10.6";
|
||||
static const String buildAt = "2023-07-28 13:50:35.251988";
|
||||
static const int modifications = 17;
|
||||
static const String buildAt = "2023-07-28 17:28:52.039761";
|
||||
static const int modifications = 10;
|
||||
}
|
||||
|
||||
@@ -44,5 +44,3 @@ const centerSizedLoading = SizedBox(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
|
||||
const loadingIcon = IconButton(onPressed: null, icon: centerLoading);
|
||||
|
||||
@@ -5,6 +5,7 @@ const myGithub = 'https://github.com/lollipopkit';
|
||||
const appHelpUrl = '$myGithub/flutter_server_box#-help';
|
||||
|
||||
// Thanks
|
||||
// If you want to change the url, please open an issue.
|
||||
const thanksMap = {
|
||||
'its-tom': 'https://github.com/its-tom',
|
||||
'RainSunMe': 'https://github.com/RainSunMe',
|
||||
@@ -15,4 +16,5 @@ const thanksMap = {
|
||||
'Aeorq': 'https://github.com/Aeorq',
|
||||
'jaychoubaby': 'https://github.com/jaychoubaby',
|
||||
'allonmymind': 'https://github.com/allonmymind',
|
||||
'azkadev': 'https://github.com/azkadev'
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"goto": "Pergi ke",
|
||||
"homeWidgetUrlConfig": "Konfigurasi URL Widget Rumah",
|
||||
"host": "Host",
|
||||
"httpFailedWithCode": "Permintaan gagal, kode status: {kode}",
|
||||
"httpFailedWithCode": "Permintaan gagal, kode status: {code}",
|
||||
"image": "Gambar",
|
||||
"imagesList": "Daftar gambar",
|
||||
"import": "Impor",
|
||||
|
||||
@@ -103,7 +103,9 @@ class _EditorPageState extends State<EditorPage> with AfterLayoutMixin {
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: CodeTheme(
|
||||
data: CodeThemeData(styles: _codeTheme ?? (isDarkMode(context) ? monokaiTheme : a11yLightTheme)),
|
||||
data: CodeThemeData(
|
||||
styles: _codeTheme ??
|
||||
(isDarkMode(context) ? monokaiTheme : a11yLightTheme)),
|
||||
child: CodeField(
|
||||
focusNode: _focusNode,
|
||||
controller: _controller,
|
||||
|
||||
@@ -280,10 +280,12 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||
showSnackBar(context, Text(_s.fieldMustNotEmpty));
|
||||
return;
|
||||
}
|
||||
locator<SftpProvider>().add(
|
||||
SftpReqItem(spi, remotePath, file.absolute.path),
|
||||
locator<SftpProvider>().add(SftpReq(
|
||||
spi,
|
||||
remotePath,
|
||||
file.absolute.path,
|
||||
SftpReqType.upload,
|
||||
);
|
||||
));
|
||||
showSnackBar(context, Text(_s.added2List));
|
||||
},
|
||||
),
|
||||
|
||||
@@ -79,7 +79,11 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
||||
|
||||
Widget _buildItem(SftpReqStatus status) {
|
||||
if (status.error != null) {
|
||||
showSnackBar(context, Text(status.error.toString()));
|
||||
final err = status.error.toString();
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 377),
|
||||
() => showSnackBar(context, Text(err)),
|
||||
);
|
||||
status.error = null;
|
||||
}
|
||||
switch (status.status) {
|
||||
@@ -92,7 +96,7 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
||||
status,
|
||||
str,
|
||||
trailing: IconButton(
|
||||
onPressed: () => shareFiles(context, [status.item.localPath]),
|
||||
onPressed: () => shareFiles(context, [status.req.localPath]),
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
),
|
||||
);
|
||||
@@ -103,18 +107,35 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
||||
return _wrapInCard(
|
||||
status,
|
||||
_s.downloadStatus(percentStr, size),
|
||||
trailing: CircularProgressIndicator(value: percent),
|
||||
trailing: SizedBox(
|
||||
height: 27,
|
||||
width: 27,
|
||||
child: CircularProgressIndicator(
|
||||
value: percent,
|
||||
),
|
||||
)
|
||||
);
|
||||
case SftpWorkerStatus.preparing:
|
||||
return _wrapInCard(status, _s.sftpDlPrepare, trailing: loadingIcon);
|
||||
return _wrapInCard(
|
||||
status,
|
||||
_s.sftpDlPrepare,
|
||||
trailing: _loading,
|
||||
);
|
||||
case SftpWorkerStatus.sshConnectted:
|
||||
return _wrapInCard(status, _s.sftpSSHConnected, trailing: loadingIcon);
|
||||
return _wrapInCard(
|
||||
status,
|
||||
_s.sftpSSHConnected,
|
||||
trailing: _loading,
|
||||
);
|
||||
default:
|
||||
return _wrapInCard(
|
||||
status,
|
||||
_s.unknown,
|
||||
trailing: const Icon(Icons.error, size: 40),
|
||||
trailing: const Icon(Icons.error),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _loading =
|
||||
SizedBox(height: 27, width: 27, child: CircularProgressIndicator());
|
||||
|
||||
@@ -174,8 +174,12 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
return;
|
||||
}
|
||||
_sftp.add(
|
||||
SftpReqItem(widget.spi, remotePath, path),
|
||||
SftpReqType.upload,
|
||||
SftpReq(
|
||||
widget.spi,
|
||||
remotePath,
|
||||
path,
|
||||
SftpReqType.upload,
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.upload_file));
|
||||
@@ -350,8 +354,13 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
final remotePath = _getRemotePath(name);
|
||||
final localPath = await _getLocalPath(remotePath);
|
||||
final completer = Completer();
|
||||
final req = SftpReqItem(widget.spi, remotePath, localPath);
|
||||
_sftp.add(req, SftpReqType.download, completer: completer);
|
||||
final req = SftpReq(
|
||||
widget.spi,
|
||||
remotePath,
|
||||
localPath,
|
||||
SftpReqType.download,
|
||||
);
|
||||
_sftp.add(req, completer: completer);
|
||||
showRoundDialog(context: context, child: centerSizedLoading);
|
||||
await completer.future;
|
||||
context.pop();
|
||||
@@ -361,7 +370,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
'SFTP edit',
|
||||
).go<String>(context);
|
||||
if (result != null) {
|
||||
_sftp.add(req, SftpReqType.upload);
|
||||
_sftp.add(SftpReq(req.spi, remotePath, localPath, SftpReqType.upload));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,12 +390,12 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
final remotePath = _getRemotePath(name);
|
||||
|
||||
_sftp.add(
|
||||
SftpReqItem(
|
||||
SftpReq(
|
||||
widget.spi,
|
||||
remotePath,
|
||||
await _getLocalPath(remotePath),
|
||||
SftpReqType.download,
|
||||
),
|
||||
SftpReqType.download,
|
||||
);
|
||||
|
||||
context.pop();
|
||||
|
||||
Reference in New Issue
Block a user