mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
feat: prompt user on host key verification (#943)
This commit is contained in:
13
.github/workflows/analysis.yml
vendored
13
.github/workflows/analysis.yml
vendored
@@ -23,19 +23,6 @@ jobs:
|
|||||||
- uses: subosito/flutter-action@v2
|
- uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
cache: true
|
|
||||||
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
|
|
||||||
|
|
||||||
- name: Cache pub dependencies
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
${{ env.PUB_CACHE }}
|
|
||||||
~/.pub-cache
|
|
||||||
key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}-${{ hashFiles('**/pubspec.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}-
|
|
||||||
${{ runner.os }}-pub-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:fl_lib/generated/l10n/lib_l10n.dart';
|
import 'package:fl_lib/generated/l10n/lib_l10n.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:server_box/core/app_navigator.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/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
@@ -87,6 +88,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
key: ValueKey(locale),
|
key: ValueKey(locale),
|
||||||
|
navigatorKey: AppNavigator.key,
|
||||||
builder: ResponsivePoints.builder,
|
builder: ResponsivePoints.builder,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
localizationsDelegates: const [LibLocalizations.delegate, ...AppLocalizations.localizationsDelegates],
|
localizationsDelegates: const [LibLocalizations.delegate, ...AppLocalizations.localizationsDelegates],
|
||||||
|
|||||||
8
lib/core/app_navigator.dart
Normal file
8
lib/core/app_navigator.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// Global navigator access used for cross-cutting flows (e.g. dialogs).
|
||||||
|
abstract final class AppNavigator {
|
||||||
|
static final key = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
static BuildContext? get context => key.currentContext;
|
||||||
|
}
|
||||||
26
lib/core/utils/host_key_helper.dart
Normal file
26
lib/core/utils/host_key_helper.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:server_box/core/utils/server.dart';
|
||||||
|
import 'package:server_box/core/utils/ssh_auth.dart';
|
||||||
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
|
Future<bool> ensureHostKeyAcceptedForSftp(BuildContext context, Spi spi) async {
|
||||||
|
final known = Stores.setting.sshKnownHostFingerprints.get();
|
||||||
|
final hostId = spi.id.isNotEmpty ? spi.id : spi.oldId;
|
||||||
|
final prefix = '$hostId::';
|
||||||
|
if (known.keys.any((key) => key.startsWith(prefix))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final (result, error) = await context.showLoadingDialog<bool>(
|
||||||
|
fn: () async {
|
||||||
|
await ensureKnownHostKey(
|
||||||
|
spi,
|
||||||
|
onKeyboardInteractive: (_) => KeybordInteractive.defaultHandle(spi, ctx: context),
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return error == null && result == true;
|
||||||
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
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/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:server_box/core/app_navigator.dart';
|
||||||
|
import 'package:server_box/core/extension/context/locale.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/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
@@ -29,7 +33,7 @@ enum GenSSHClientStatus { socket, key, pwd }
|
|||||||
String getPrivateKey(String id) {
|
String getPrivateKey(String id) {
|
||||||
final pki = Stores.key.fetchOne(id);
|
final pki = Stores.key.fetchOne(id);
|
||||||
if (pki == null) {
|
if (pki == null) {
|
||||||
throw SSHErr(type: SSHErrType.noPrivateKey, message: 'key [$id] not found');
|
throw SSHErr(type: SSHErrType.noPrivateKey, message: l10n.privateKeyNotFoundFmt(id));
|
||||||
}
|
}
|
||||||
return pki.key;
|
return pki.key;
|
||||||
}
|
}
|
||||||
@@ -52,9 +56,16 @@ Future<SSHClient> genClient(
|
|||||||
|
|
||||||
/// Handle keyboard-interactive authentication
|
/// Handle keyboard-interactive authentication
|
||||||
SSHUserInfoRequestHandler? onKeyboardInteractive,
|
SSHUserInfoRequestHandler? onKeyboardInteractive,
|
||||||
|
Map<String, String>? knownHostFingerprints,
|
||||||
|
void Function(String storageKey, String fingerprintHex)? onHostKeyAccepted,
|
||||||
|
Future<bool> Function(HostKeyPromptInfo info)? onHostKeyPrompt,
|
||||||
}) async {
|
}) async {
|
||||||
onStatus?.call(GenSSHClientStatus.socket);
|
onStatus?.call(GenSSHClientStatus.socket);
|
||||||
|
|
||||||
|
final hostKeyCache = Map<String, String>.from(knownHostFingerprints ?? _loadKnownHostFingerprints());
|
||||||
|
final hostKeyPersist = onHostKeyAccepted ?? _persistHostKeyFingerprint;
|
||||||
|
final hostKeyPrompt = onHostKeyPrompt ?? _defaultHostKeyPrompt;
|
||||||
|
|
||||||
String? alterUser;
|
String? alterUser;
|
||||||
|
|
||||||
final socket = await () async {
|
final socket = await () async {
|
||||||
@@ -66,7 +77,14 @@ Future<SSHClient> genClient(
|
|||||||
if (spi.jumpId != null) return Stores.server.box.get(spi.jumpId);
|
if (spi.jumpId != null) return Stores.server.box.get(spi.jumpId);
|
||||||
}();
|
}();
|
||||||
if (jumpSpi_ != null) {
|
if (jumpSpi_ != null) {
|
||||||
final jumpClient = await genClient(jumpSpi_, privateKey: jumpPrivateKey, timeout: timeout);
|
final jumpClient = await genClient(
|
||||||
|
jumpSpi_,
|
||||||
|
privateKey: jumpPrivateKey,
|
||||||
|
timeout: timeout,
|
||||||
|
knownHostFingerprints: hostKeyCache,
|
||||||
|
onHostKeyAccepted: hostKeyPersist,
|
||||||
|
onHostKeyPrompt: onHostKeyPrompt,
|
||||||
|
);
|
||||||
|
|
||||||
return await jumpClient.forwardLocal(spi.ip, spi.port);
|
return await jumpClient.forwardLocal(spi.ip, spi.port);
|
||||||
}
|
}
|
||||||
@@ -88,6 +106,13 @@ Future<SSHClient> genClient(
|
|||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
final hostKeyVerifier = _HostKeyVerifier(
|
||||||
|
spi: spi,
|
||||||
|
cache: hostKeyCache,
|
||||||
|
persistCallback: hostKeyPersist,
|
||||||
|
prompt: hostKeyPrompt,
|
||||||
|
);
|
||||||
|
|
||||||
final keyId = spi.keyId;
|
final keyId = spi.keyId;
|
||||||
if (keyId == null) {
|
if (keyId == null) {
|
||||||
onStatus?.call(GenSSHClientStatus.pwd);
|
onStatus?.call(GenSSHClientStatus.pwd);
|
||||||
@@ -96,9 +121,7 @@ Future<SSHClient> genClient(
|
|||||||
username: alterUser ?? spi.user,
|
username: alterUser ?? spi.user,
|
||||||
onPasswordRequest: () => spi.pwd,
|
onPasswordRequest: () => spi.pwd,
|
||||||
onUserInfoRequest: onKeyboardInteractive,
|
onUserInfoRequest: onKeyboardInteractive,
|
||||||
|
onVerifyHostKey: hostKeyVerifier.call,
|
||||||
/// TODO: verify host key
|
|
||||||
onVerifyHostKey: (type, fingerprint) => true,
|
|
||||||
// printDebug: debugPrint,
|
// printDebug: debugPrint,
|
||||||
// printTrace: debugPrint,
|
// printTrace: debugPrint,
|
||||||
);
|
);
|
||||||
@@ -112,10 +135,220 @@ Future<SSHClient> genClient(
|
|||||||
// Must use [compute] here, instead of [Computer.shared.start]
|
// Must use [compute] here, instead of [Computer.shared.start]
|
||||||
identities: await compute(loadIndentity, privateKey),
|
identities: await compute(loadIndentity, privateKey),
|
||||||
onUserInfoRequest: onKeyboardInteractive,
|
onUserInfoRequest: onKeyboardInteractive,
|
||||||
|
onVerifyHostKey: hostKeyVerifier.call,
|
||||||
/// TODO: verify host key
|
|
||||||
onVerifyHostKey: (type, fingerprint) => true,
|
|
||||||
// printDebug: debugPrint,
|
// printDebug: debugPrint,
|
||||||
// printTrace: debugPrint,
|
// printTrace: debugPrint,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef _HostKeyPersistCallback = void Function(String storageKey, String fingerprintHex);
|
||||||
|
|
||||||
|
class HostKeyPromptInfo {
|
||||||
|
HostKeyPromptInfo({
|
||||||
|
required this.spi,
|
||||||
|
required this.keyType,
|
||||||
|
required this.fingerprintHex,
|
||||||
|
required this.fingerprintBase64,
|
||||||
|
required this.isMismatch,
|
||||||
|
this.previousFingerprintHex,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Spi spi;
|
||||||
|
final String keyType;
|
||||||
|
final String fingerprintHex;
|
||||||
|
final String fingerprintBase64;
|
||||||
|
final bool isMismatch;
|
||||||
|
final String? previousFingerprintHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HostKeyVerifier {
|
||||||
|
_HostKeyVerifier({
|
||||||
|
required this.spi,
|
||||||
|
required Map<String, String> cache,
|
||||||
|
required this.prompt,
|
||||||
|
this.persistCallback,
|
||||||
|
}) : _cache = cache;
|
||||||
|
|
||||||
|
final Spi spi;
|
||||||
|
final Map<String, String> _cache;
|
||||||
|
final _HostKeyPersistCallback? persistCallback;
|
||||||
|
final Future<bool> Function(HostKeyPromptInfo info) prompt;
|
||||||
|
|
||||||
|
Future<bool> call(String keyType, Uint8List fingerprintBytes) async {
|
||||||
|
final storageKey = _hostKeyStorageKey(spi, keyType);
|
||||||
|
final fingerprintHex = _fingerprintToHex(fingerprintBytes);
|
||||||
|
final fingerprintBase64 = _fingerprintToBase64(fingerprintBytes);
|
||||||
|
final existing = _cache[storageKey];
|
||||||
|
|
||||||
|
if (existing == null) {
|
||||||
|
final accepted = await prompt(
|
||||||
|
HostKeyPromptInfo(
|
||||||
|
spi: spi,
|
||||||
|
keyType: keyType,
|
||||||
|
fingerprintHex: fingerprintHex,
|
||||||
|
fingerprintBase64: fingerprintBase64,
|
||||||
|
isMismatch: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!accepted) {
|
||||||
|
Loggers.app.warning('User rejected new SSH host key for ${spi.name} ($keyType).');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_cache[storageKey] = fingerprintHex;
|
||||||
|
persistCallback?.call(storageKey, fingerprintHex);
|
||||||
|
Loggers.app.info('Trusted SSH host key for ${spi.name} ($keyType).');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing == fingerprintHex) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final accepted = await prompt(
|
||||||
|
HostKeyPromptInfo(
|
||||||
|
spi: spi,
|
||||||
|
keyType: keyType,
|
||||||
|
fingerprintHex: fingerprintHex,
|
||||||
|
fingerprintBase64: fingerprintBase64,
|
||||||
|
isMismatch: true,
|
||||||
|
previousFingerprintHex: existing,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!accepted) {
|
||||||
|
Loggers.app.warning(
|
||||||
|
'SSH host key mismatch for ${spi.name}',
|
||||||
|
'expected $existing but received $fingerprintHex ($keyType)',
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache[storageKey] = fingerprintHex;
|
||||||
|
persistCallback?.call(storageKey, fingerprintHex);
|
||||||
|
Loggers.app.warning('Updated stored SSH host key for ${spi.name} ($keyType) after user confirmation.');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> _loadKnownHostFingerprints() {
|
||||||
|
try {
|
||||||
|
final prop = Stores.setting.sshKnownHostFingerprints;
|
||||||
|
return Map<String, String>.from(prop.get());
|
||||||
|
} catch (e, stack) {
|
||||||
|
Loggers.app.warning('Load SSH host key fingerprints failed', e, stack);
|
||||||
|
return <String, String>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _persistHostKeyFingerprint(String storageKey, String fingerprintHex) {
|
||||||
|
try {
|
||||||
|
final prop = Stores.setting.sshKnownHostFingerprints;
|
||||||
|
final updated = Map<String, String>.from(prop.get());
|
||||||
|
if (updated[storageKey] == fingerprintHex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updated[storageKey] = fingerprintHex;
|
||||||
|
prop.put(updated);
|
||||||
|
Loggers.app.info('Stored SSH host key fingerprint for $storageKey');
|
||||||
|
} catch (e, stack) {
|
||||||
|
Loggers.app.warning('Persist SSH host key fingerprint failed', e, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _defaultHostKeyPrompt(HostKeyPromptInfo info) async {
|
||||||
|
final ctx = AppNavigator.context;
|
||||||
|
if (ctx == null) {
|
||||||
|
Loggers.app.warning('Host key prompt skipped: navigator context unavailable.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final hostLine = '${info.spi.user}@${info.spi.ip}:${info.spi.port}';
|
||||||
|
final description = info.isMismatch
|
||||||
|
? l10n.sshHostKeyChangedDesc(info.spi.name)
|
||||||
|
: l10n.sshHostKeyNewDesc(info.spi.name);
|
||||||
|
|
||||||
|
final result = await ctx.showRoundDialog<bool>(
|
||||||
|
title: libL10n.attention,
|
||||||
|
barrierDismiss: false,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(description),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
SelectableText('${l10n.server}: ${info.spi.name}'),
|
||||||
|
SelectableText('${libL10n.addr}: $hostLine'),
|
||||||
|
SelectableText('${l10n.sshHostKeyType}: ${info.keyType}'),
|
||||||
|
SelectableText(l10n.sshHostKeyFingerprintMd5Hex(info.fingerprintHex)),
|
||||||
|
SelectableText(l10n.sshHostKeyFingerprintMd5Base64(info.fingerprintBase64)),
|
||||||
|
if (info.previousFingerprintHex != null) ...[
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
SelectableText(l10n.sshHostKeyStoredFingerprint(info.previousFingerprintHex!)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => ctx.pop(false), child: Text(libL10n.cancel)),
|
||||||
|
TextButton(onPressed: () => ctx.pop(true), child: Text(libL10n.ok)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return result ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> ensureKnownHostKey(
|
||||||
|
Spi spi, {
|
||||||
|
Duration timeout = const Duration(seconds: 5),
|
||||||
|
SSHUserInfoRequestHandler? onKeyboardInteractive,
|
||||||
|
}) async {
|
||||||
|
final cache = _loadKnownHostFingerprints();
|
||||||
|
if (_hasKnownHostFingerprintForSpi(spi, cache)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final jumpSpi = spi.jumpId != null ? Stores.server.box.get(spi.jumpId) : null;
|
||||||
|
if (jumpSpi != null && !_hasKnownHostFingerprintForSpi(jumpSpi, cache)) {
|
||||||
|
await ensureKnownHostKey(
|
||||||
|
jumpSpi,
|
||||||
|
timeout: timeout,
|
||||||
|
onKeyboardInteractive: onKeyboardInteractive,
|
||||||
|
);
|
||||||
|
cache.addAll(_loadKnownHostFingerprints());
|
||||||
|
if (_hasKnownHostFingerprintForSpi(spi, cache)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final client = await genClient(
|
||||||
|
spi,
|
||||||
|
timeout: timeout,
|
||||||
|
onKeyboardInteractive: onKeyboardInteractive,
|
||||||
|
knownHostFingerprints: cache,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.authenticated;
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _hasKnownHostFingerprintForSpi(Spi spi, Map<String, String> cache) {
|
||||||
|
final prefix = '${_hostIdentifier(spi)}::';
|
||||||
|
return cache.keys.any((key) => key.startsWith(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
String _hostKeyStorageKey(Spi spi, String keyType) {
|
||||||
|
final base = _hostIdentifier(spi);
|
||||||
|
return '$base::$keyType';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _hostIdentifier(Spi spi) => spi.id.isNotEmpty ? spi.id : spi.oldId;
|
||||||
|
|
||||||
|
String _fingerprintToHex(Uint8List fingerprint) {
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
for (var i = 0; i < fingerprint.length; i++) {
|
||||||
|
if (i > 0) buffer.write(':');
|
||||||
|
buffer.write(fingerprint[i].toRadixString(16).padLeft(2, '0'));
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _fingerprintToBase64(Uint8List fingerprint) => base64.encode(fingerprint);
|
||||||
|
|||||||
@@ -72,6 +72,18 @@ class SettingStore extends HiveStore {
|
|||||||
|
|
||||||
late final editorFontSize = propertyDefault('editorFontSize', 12.5);
|
late final editorFontSize = propertyDefault('editorFontSize', 12.5);
|
||||||
|
|
||||||
|
/// Trusted SSH host key fingerprints keyed by `serverId::keyType`.
|
||||||
|
late final sshKnownHostFingerprints = propertyDefault<Map<String, String>>(
|
||||||
|
'sshKnownHostFingerprints',
|
||||||
|
const {},
|
||||||
|
fromObj: (raw) {
|
||||||
|
if (raw is Map) {
|
||||||
|
return raw.map((key, value) => MapEntry(key.toString(), value.toString()));
|
||||||
|
}
|
||||||
|
return <String, String>{};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Editor theme
|
// Editor theme
|
||||||
late final editorTheme = propertyDefault('editorTheme', Defaults.editorTheme);
|
late final editorTheme = propertyDefault('editorTheme', Defaults.editorTheme);
|
||||||
|
|
||||||
|
|||||||
@@ -1148,6 +1148,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Private Key'**
|
/// **'Private Key'**
|
||||||
String get privateKey;
|
String get privateKey;
|
||||||
|
|
||||||
|
/// No description provided for @privateKeyNotFoundFmt.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Private key [{keyId}] not found.'**
|
||||||
|
String privateKeyNotFoundFmt(Object keyId);
|
||||||
|
|
||||||
/// No description provided for @process.
|
/// No description provided for @process.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -1472,6 +1478,42 @@ abstract class AppLocalizations {
|
|||||||
/// **'Imported {count} servers from SSH config'**
|
/// **'Imported {count} servers from SSH config'**
|
||||||
String sshConfigImported(Object count);
|
String sshConfigImported(Object count);
|
||||||
|
|
||||||
|
/// No description provided for @sshHostKeyChangedDesc.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'The SSH host key changed for {serverName}. Only continue if you trust this server.'**
|
||||||
|
String sshHostKeyChangedDesc(Object serverName);
|
||||||
|
|
||||||
|
/// No description provided for @sshHostKeyFingerprintMd5Base64.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Fingerprint (MD5 base64): {fingerprint}'**
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint);
|
||||||
|
|
||||||
|
/// No description provided for @sshHostKeyFingerprintMd5Hex.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Fingerprint (MD5 hex): {fingerprint}'**
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint);
|
||||||
|
|
||||||
|
/// Label for the SSH host key type displayed in the host key verification dialog.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'SSH host key type'**
|
||||||
|
String get sshHostKeyType;
|
||||||
|
|
||||||
|
/// No description provided for @sshHostKeyNewDesc.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'A new SSH host key was received from {serverName}. Review the fingerprint before trusting.'**
|
||||||
|
String sshHostKeyNewDesc(Object serverName);
|
||||||
|
|
||||||
|
/// No description provided for @sshHostKeyStoredFingerprint.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Stored fingerprint: {fingerprint}'**
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint);
|
||||||
|
|
||||||
/// No description provided for @sshConfigManualSelect.
|
/// No description provided for @sshConfigManualSelect.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -581,6 +581,11 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Private Key';
|
String get privateKey => 'Private Key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Privater Schlüssel [$keyId] wurde nicht gefunden.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Prozess';
|
String get process => 'Prozess';
|
||||||
|
|
||||||
@@ -764,6 +769,34 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
return '$count Server aus SSH-Konfiguration importiert';
|
return '$count Server aus SSH-Konfiguration importiert';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'Der SSH-Hostschlüssel für $serverName hat sich geändert. Fahren Sie nur fort, wenn Sie diesem Server vertrauen.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Fingerabdruck (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Fingerabdruck (MD5 Hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'SSH-Hostschlüsseltyp';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Ein neuer SSH-Hostschlüssel wurde von $serverName empfangen. Prüfen Sie den Fingerabdruck, bevor Sie vertrauen.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Gespeicherter Fingerabdruck: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Möchten Sie die SSH-Konfigurationsdatei manuell auswählen?';
|
'Möchten Sie die SSH-Konfigurationsdatei manuell auswählen?';
|
||||||
|
|||||||
@@ -578,6 +578,11 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Private Key';
|
String get privateKey => 'Private Key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Private key [$keyId] not found.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Process';
|
String get process => 'Process';
|
||||||
|
|
||||||
@@ -758,6 +763,34 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
return 'Imported $count servers from SSH config';
|
return 'Imported $count servers from SSH config';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'The SSH host key changed for $serverName. Only continue if you trust this server.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Fingerprint (MD5 base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Fingerprint (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'SSH host key type';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'A new SSH host key was received from $serverName. Review the fingerprint before trusting.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Stored fingerprint: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Would you like to select the SSH config file manually?';
|
'Would you like to select the SSH config file manually?';
|
||||||
|
|||||||
@@ -583,6 +583,11 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Llave privada';
|
String get privateKey => 'Llave privada';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'No se encontró la clave privada [$keyId].';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Proceso';
|
String get process => 'Proceso';
|
||||||
|
|
||||||
@@ -767,6 +772,34 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
return 'Se importaron $count servidores desde la configuración SSH';
|
return 'Se importaron $count servidores desde la configuración SSH';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'La clave de host SSH de $serverName ha cambiado. Continúa solo si confías en este servidor.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Huella (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Huella (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'Tipo de clave de host SSH';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Se recibió una nueva clave de host SSH de $serverName. Revisa la huella antes de confiar.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Huella almacenada: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'¿Te gustaría seleccionar manualmente el archivo de configuración SSH?';
|
'¿Te gustaría seleccionar manualmente el archivo de configuración SSH?';
|
||||||
|
|||||||
@@ -585,6 +585,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Clé privée';
|
String get privateKey => 'Clé privée';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Clé privée [$keyId] introuvable.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Processus';
|
String get process => 'Processus';
|
||||||
|
|
||||||
@@ -769,6 +774,34 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
return '$count serveurs importés depuis la configuration SSH';
|
return '$count serveurs importés depuis la configuration SSH';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'La clé d\'hôte SSH de $serverName a changé. Ne continuez que si vous faites confiance à ce serveur.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Empreinte (MD5 Base64) : $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Empreinte (MD5 hex) : $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'Type de clé d\'hôte SSH';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Une nouvelle clé d\'hôte SSH a été reçue de $serverName. Vérifiez l\'empreinte avant de faire confiance.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Empreinte enregistrée : $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Souhaitez-vous sélectionner manuellement le fichier de configuration SSH ?';
|
'Souhaitez-vous sélectionner manuellement le fichier de configuration SSH ?';
|
||||||
|
|||||||
@@ -578,6 +578,11 @@ class AppLocalizationsId extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Kunci Pribadi';
|
String get privateKey => 'Kunci Pribadi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Kunci privat [$keyId] tidak ditemukan.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Proses';
|
String get process => 'Proses';
|
||||||
|
|
||||||
@@ -759,6 +764,34 @@ class AppLocalizationsId extends AppLocalizations {
|
|||||||
return 'Berhasil mengimpor $count server dari konfigurasi SSH';
|
return 'Berhasil mengimpor $count server dari konfigurasi SSH';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'Kunci host SSH untuk $serverName telah berubah. Lanjutkan hanya jika Anda mempercayai server ini.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Sidik jari (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Sidik jari (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'Jenis kunci host SSH';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Kunci host SSH baru diterima dari $serverName. Periksa sidik jarinya sebelum mempercayai.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Sidik jari tersimpan: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Apakah Anda ingin memilih file konfigurasi SSH secara manual?';
|
'Apakah Anda ingin memilih file konfigurasi SSH secara manual?';
|
||||||
|
|||||||
@@ -560,6 +560,11 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => '秘密鍵';
|
String get privateKey => '秘密鍵';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return '秘密鍵 [$keyId] が見つかりません。';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'プロセス';
|
String get process => 'プロセス';
|
||||||
|
|
||||||
@@ -737,6 +742,34 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
return 'SSH設定から$count個のサーバーをインポートしました';
|
return 'SSH設定から$count個のサーバーをインポートしました';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return '$serverName の SSH ホスト鍵が変更されました。このサーバーを信頼できる場合のみ続行してください。';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'フィンガープリント (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'フィンガープリント (MD5 16進): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'SSH ホストキーの種類';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return '$serverName から新しい SSH ホスト鍵を受信しました。信頼する前にフィンガープリントを確認してください。';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return '保存済みフィンガープリント: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect => 'SSH設定ファイルを手動で選択しますか?';
|
String get sshConfigManualSelect => 'SSH設定ファイルを手動で選択しますか?';
|
||||||
|
|
||||||
|
|||||||
@@ -580,6 +580,11 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Privésleutel';
|
String get privateKey => 'Privésleutel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Privésleutel [$keyId] niet gevonden.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Proces';
|
String get process => 'Proces';
|
||||||
|
|
||||||
@@ -763,6 +768,34 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
return '$count servers geïmporteerd uit SSH-configuratie';
|
return '$count servers geïmporteerd uit SSH-configuratie';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'De SSH-hostsleutel voor $serverName is gewijzigd. Ga alleen verder als u deze server vertrouwt.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Vingerafdruk (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Vingerafdruk (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'Type SSH-hostsleutel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Er is een nieuwe SSH-hostsleutel ontvangen van $serverName. Controleer de vingerafdruk voordat u vertrouwt.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Opgeslagen vingerafdruk: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Wilt u het SSH-configuratiebestand handmatig selecteren?';
|
'Wilt u het SSH-configuratiebestand handmatig selecteren?';
|
||||||
|
|||||||
@@ -578,6 +578,11 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Chave privada';
|
String get privateKey => 'Chave privada';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Chave privada [$keyId] não encontrada.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Processo';
|
String get process => 'Processo';
|
||||||
|
|
||||||
@@ -759,6 +764,34 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
return 'Importados $count servidores da configuração SSH';
|
return 'Importados $count servidores da configuração SSH';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'A chave de host SSH de $serverName foi alterada. Continue apenas se confiar neste servidor.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Impressão digital (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Impressão digital (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'Tipo de chave de host SSH';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Uma nova chave de host SSH foi recebida de $serverName. Verifique a impressão digital antes de confiar.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Impressão digital armazenada: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Gostaria de selecionar manualmente o arquivo de configuração SSH?';
|
'Gostaria de selecionar manualmente o arquivo de configuração SSH?';
|
||||||
|
|||||||
@@ -581,6 +581,11 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Приватный ключ';
|
String get privateKey => 'Приватный ключ';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Закрытый ключ [$keyId] не найден.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Процесс';
|
String get process => 'Процесс';
|
||||||
|
|
||||||
@@ -764,6 +769,34 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
return 'Импортировано $count серверов из SSH-конфигурации';
|
return 'Импортировано $count серверов из SSH-конфигурации';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'SSH-ключ хоста для $serverName изменился. Продолжайте только если доверяете этому серверу.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Отпечаток (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Отпечаток (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'Тип ключа хоста SSH';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Получен новый SSH-ключ хоста от $serverName. Проверьте отпечаток перед продолжением.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Сохранённый отпечаток: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Хотели бы вы вручную выбрать файл конфигурации SSH?';
|
'Хотели бы вы вручную выбрать файл конфигурации SSH?';
|
||||||
|
|||||||
@@ -578,6 +578,11 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Özel Anahtar';
|
String get privateKey => 'Özel Anahtar';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Özel anahtar [$keyId] bulunamadı.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'İşlem';
|
String get process => 'İşlem';
|
||||||
|
|
||||||
@@ -760,6 +765,34 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
return 'SSH yapılandırmasından $count sunucu içe aktarıldı';
|
return 'SSH yapılandırmasından $count sunucu içe aktarıldı';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return '$serverName için SSH ana bilgisayar anahtarı değişti. Yalnızca bu sunucuya güveniyorsanız devam edin.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Parmak izi (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Parmak izi (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'SSH ana bilgisayar anahtarı türü';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return '$serverName üzerinden yeni bir SSH ana bilgisayar anahtarı alındı. Güvenmeden önce parmak izini kontrol edin.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Kaydedilen parmak izi: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'SSH yapılandırma dosyasını manuel olarak seçmek ister misiniz?';
|
'SSH yapılandırma dosyasını manuel olarak seçmek ister misiniz?';
|
||||||
|
|||||||
@@ -582,6 +582,11 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => 'Приватний ключ';
|
String get privateKey => 'Приватний ключ';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return 'Приватний ключ [$keyId] не знайдено.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => 'Процес';
|
String get process => 'Процес';
|
||||||
|
|
||||||
@@ -764,6 +769,34 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
return 'Імпортовано $count серверів з SSH-конфігурації';
|
return 'Імпортовано $count серверів з SSH-конфігурації';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return 'SSH-ключ хоста для $serverName змінено. Продовжуйте лише якщо довіряєте цьому серверу.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return 'Відбиток (MD5 Base64): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return 'Відбиток (MD5 hex): $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'Тип ключа хоста SSH';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return 'Отримано новий SSH-ключ хоста від $serverName. Перевірте відбиток перед тим, як довіряти.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return 'Збережений відбиток: $fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect =>
|
String get sshConfigManualSelect =>
|
||||||
'Чи хочете ви вручну вибрати файл конфігурації SSH?';
|
'Чи хочете ви вручну вибрати файл конфігурації SSH?';
|
||||||
|
|||||||
@@ -554,6 +554,11 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => '私钥';
|
String get privateKey => '私钥';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return '未找到私钥 [$keyId]。';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => '进程';
|
String get process => '进程';
|
||||||
|
|
||||||
@@ -727,6 +732,34 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
return '从 SSH 配置导入了 $count 个服务器';
|
return '从 SSH 配置导入了 $count 个服务器';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return '服务器 $serverName 的 SSH 主机密钥已更改,仅在信任该服务器时继续。';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return '指纹(MD5 Base64):$fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return '指纹(MD5 十六进制):$fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'SSH 主机密钥类型';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return '收到来自 $serverName 的新 SSH 主机密钥,在信任前请检查指纹。';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return '已存储的指纹:$fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect => '是否要手动选择 SSH 配置文件?';
|
String get sshConfigManualSelect => '是否要手动选择 SSH 配置文件?';
|
||||||
|
|
||||||
@@ -1472,6 +1505,11 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
|
|||||||
@override
|
@override
|
||||||
String get privateKey => '私鑰';
|
String get privateKey => '私鑰';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyNotFoundFmt(Object keyId) {
|
||||||
|
return '未找到私鑰 [$keyId]。';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get process => '處理程序';
|
String get process => '處理程序';
|
||||||
|
|
||||||
@@ -1645,6 +1683,34 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
|
|||||||
return '已從SSH設定匯入$count個伺服器';
|
return '已從SSH設定匯入$count個伺服器';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyChangedDesc(Object serverName) {
|
||||||
|
return '伺服器 $serverName 的 SSH 主機金鑰已變更,僅在信任該伺服器時繼續。';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Base64(Object fingerprint) {
|
||||||
|
return '指紋(MD5 Base64):$fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyFingerprintMd5Hex(Object fingerprint) {
|
||||||
|
return '指紋(MD5 十六進位):$fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sshHostKeyType => 'SSH 主機金鑰類型';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyNewDesc(Object serverName) {
|
||||||
|
return '收到來自 $serverName 的新 SSH 主機金鑰,信任前請先檢查指紋。';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String sshHostKeyStoredFingerprint(Object fingerprint) {
|
||||||
|
return '已儲存的指紋:$fingerprint';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sshConfigManualSelect => '是否要手動選擇 SSH 設定檔案?';
|
String get sshConfigManualSelect => '是否要手動選擇 SSH 設定檔案?';
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferDiskAmount": "Festplattenkapazität vorrangig anzeigen",
|
"preferDiskAmount": "Festplattenkapazität vorrangig anzeigen",
|
||||||
"privateKey": "Private Key",
|
"privateKey": "Private Key",
|
||||||
|
"privateKeyNotFoundFmt": "Privater Schlüssel [{keyId}] wurde nicht gefunden.",
|
||||||
"process": "Prozess",
|
"process": "Prozess",
|
||||||
"prune": "Beschneiden",
|
"prune": "Beschneiden",
|
||||||
"pushToken": "Push Token",
|
"pushToken": "Push Token",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "Möchten Sie die Berechtigung erteilen, ~/.ssh/config zu lesen und Server-Einstellungen automatisch zu importieren?",
|
"sshConfigImportPermission": "Möchten Sie die Berechtigung erteilen, ~/.ssh/config zu lesen und Server-Einstellungen automatisch zu importieren?",
|
||||||
"sshConfigImportTip": "Bei der ersten Server-Erstellung zum Lesen von ~/.ssh/config auffordern",
|
"sshConfigImportTip": "Bei der ersten Server-Erstellung zum Lesen von ~/.ssh/config auffordern",
|
||||||
"sshConfigImported": "{count} Server aus SSH-Konfiguration importiert",
|
"sshConfigImported": "{count} Server aus SSH-Konfiguration importiert",
|
||||||
|
"sshHostKeyChangedDesc": "Der SSH-Hostschlüssel für {serverName} hat sich geändert. Fahren Sie nur fort, wenn Sie diesem Server vertrauen.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Fingerabdruck (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Fingerabdruck (MD5 Hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "SSH-Hostschlüsseltyp",
|
||||||
|
"sshHostKeyNewDesc": "Ein neuer SSH-Hostschlüssel wurde von {serverName} empfangen. Prüfen Sie den Fingerabdruck, bevor Sie vertrauen.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Gespeicherter Fingerabdruck: {fingerprint}",
|
||||||
"sshConfigManualSelect": "Möchten Sie die SSH-Konfigurationsdatei manuell auswählen?",
|
"sshConfigManualSelect": "Möchten Sie die SSH-Konfigurationsdatei manuell auswählen?",
|
||||||
"sshConfigNoServers": "Keine Server in der SSH-Konfiguration gefunden",
|
"sshConfigNoServers": "Keine Server in der SSH-Konfiguration gefunden",
|
||||||
"sshConfigPermissionDenied": "Aufgrund der macOS-Berechtigungen kann nicht auf die SSH-Konfigurationsdatei zugegriffen werden.",
|
"sshConfigPermissionDenied": "Aufgrund der macOS-Berechtigungen kann nicht auf die SSH-Konfigurationsdatei zugegriffen werden.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferDiskAmount": "Prioritize displaying disk capacity",
|
"preferDiskAmount": "Prioritize displaying disk capacity",
|
||||||
"privateKey": "Private Key",
|
"privateKey": "Private Key",
|
||||||
|
"privateKeyNotFoundFmt": "Private key [{keyId}] not found.",
|
||||||
"process": "Process",
|
"process": "Process",
|
||||||
"prune": "Prune",
|
"prune": "Prune",
|
||||||
"pushToken": "Push token",
|
"pushToken": "Push token",
|
||||||
@@ -225,6 +226,15 @@
|
|||||||
"sshConfigImportPermission": "Would you like to give permission to read ~/.ssh/config and automatically import server settings?",
|
"sshConfigImportPermission": "Would you like to give permission to read ~/.ssh/config and automatically import server settings?",
|
||||||
"sshConfigImportTip": "Prompt to read ~/.ssh/config on first server creation",
|
"sshConfigImportTip": "Prompt to read ~/.ssh/config on first server creation",
|
||||||
"sshConfigImported": "Imported {count} servers from SSH config",
|
"sshConfigImported": "Imported {count} servers from SSH config",
|
||||||
|
"sshHostKeyChangedDesc": "The SSH host key changed for {serverName}. Only continue if you trust this server.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Fingerprint (MD5 base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Fingerprint (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "SSH host key type",
|
||||||
|
"@sshHostKeyType": {
|
||||||
|
"description": "Label for the SSH host key type displayed in the host key verification dialog."
|
||||||
|
},
|
||||||
|
"sshHostKeyNewDesc": "A new SSH host key was received from {serverName}. Review the fingerprint before trusting.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Stored fingerprint: {fingerprint}",
|
||||||
"sshConfigManualSelect": "Would you like to select the SSH config file manually?",
|
"sshConfigManualSelect": "Would you like to select the SSH config file manually?",
|
||||||
"sshConfigNoServers": "No servers found in SSH config",
|
"sshConfigNoServers": "No servers found in SSH config",
|
||||||
"sshConfigPermissionDenied": "Cannot access SSH config file due to macOS permissions.",
|
"sshConfigPermissionDenied": "Cannot access SSH config file due to macOS permissions.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Puerto",
|
"port": "Puerto",
|
||||||
"preferDiskAmount": "Priorizar la visualización de la capacidad del disco",
|
"preferDiskAmount": "Priorizar la visualización de la capacidad del disco",
|
||||||
"privateKey": "Llave privada",
|
"privateKey": "Llave privada",
|
||||||
|
"privateKeyNotFoundFmt": "No se encontró la clave privada [{keyId}].",
|
||||||
"process": "Proceso",
|
"process": "Proceso",
|
||||||
"prune": "Podar",
|
"prune": "Podar",
|
||||||
"pushToken": "Token de notificaciones",
|
"pushToken": "Token de notificaciones",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "¿Te gustaría dar permiso para leer ~/.ssh/config e importar automáticamente la configuración de servidores?",
|
"sshConfigImportPermission": "¿Te gustaría dar permiso para leer ~/.ssh/config e importar automáticamente la configuración de servidores?",
|
||||||
"sshConfigImportTip": "Sugerencia para leer ~/.ssh/config al crear el primer servidor",
|
"sshConfigImportTip": "Sugerencia para leer ~/.ssh/config al crear el primer servidor",
|
||||||
"sshConfigImported": "Se importaron {count} servidores desde la configuración SSH",
|
"sshConfigImported": "Se importaron {count} servidores desde la configuración SSH",
|
||||||
|
"sshHostKeyChangedDesc": "La clave de host SSH de {serverName} ha cambiado. Continúa solo si confías en este servidor.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Huella (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Huella (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "Tipo de clave de host SSH",
|
||||||
|
"sshHostKeyNewDesc": "Se recibió una nueva clave de host SSH de {serverName}. Revisa la huella antes de confiar.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Huella almacenada: {fingerprint}",
|
||||||
"sshConfigManualSelect": "¿Te gustaría seleccionar manualmente el archivo de configuración SSH?",
|
"sshConfigManualSelect": "¿Te gustaría seleccionar manualmente el archivo de configuración SSH?",
|
||||||
"sshConfigNoServers": "No se encontraron servidores en la configuración SSH",
|
"sshConfigNoServers": "No se encontraron servidores en la configuración SSH",
|
||||||
"sshConfigPermissionDenied": "No se puede acceder al archivo de configuración SSH debido a los permisos de macOS.",
|
"sshConfigPermissionDenied": "No se puede acceder al archivo de configuración SSH debido a los permisos de macOS.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferDiskAmount": "Prioriser l’affichage de la capacité du disque",
|
"preferDiskAmount": "Prioriser l’affichage de la capacité du disque",
|
||||||
"privateKey": "Clé privée",
|
"privateKey": "Clé privée",
|
||||||
|
"privateKeyNotFoundFmt": "Clé privée [{keyId}] introuvable.",
|
||||||
"process": "Processus",
|
"process": "Processus",
|
||||||
"prune": "Élaguer",
|
"prune": "Élaguer",
|
||||||
"pushToken": "Jeton d'identification",
|
"pushToken": "Jeton d'identification",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "Souhaitez-vous donner la permission de lire ~/.ssh/config et d'importer automatiquement les paramètres du serveur ?",
|
"sshConfigImportPermission": "Souhaitez-vous donner la permission de lire ~/.ssh/config et d'importer automatiquement les paramètres du serveur ?",
|
||||||
"sshConfigImportTip": "Proposer de lire ~/.ssh/config lors de la première création de serveur",
|
"sshConfigImportTip": "Proposer de lire ~/.ssh/config lors de la première création de serveur",
|
||||||
"sshConfigImported": "{count} serveurs importés depuis la configuration SSH",
|
"sshConfigImported": "{count} serveurs importés depuis la configuration SSH",
|
||||||
|
"sshHostKeyChangedDesc": "La clé d'hôte SSH de {serverName} a changé. Ne continuez que si vous faites confiance à ce serveur.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Empreinte (MD5 Base64) : {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Empreinte (MD5 hex) : {fingerprint}",
|
||||||
|
"sshHostKeyType": "Type de clé d'hôte SSH",
|
||||||
|
"sshHostKeyNewDesc": "Une nouvelle clé d'hôte SSH a été reçue de {serverName}. Vérifiez l'empreinte avant de faire confiance.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Empreinte enregistrée : {fingerprint}",
|
||||||
"sshConfigManualSelect": "Souhaitez-vous sélectionner manuellement le fichier de configuration SSH ?",
|
"sshConfigManualSelect": "Souhaitez-vous sélectionner manuellement le fichier de configuration SSH ?",
|
||||||
"sshConfigNoServers": "Aucun serveur trouvé dans la configuration SSH",
|
"sshConfigNoServers": "Aucun serveur trouvé dans la configuration SSH",
|
||||||
"sshConfigPermissionDenied": "Impossible d'accéder au fichier de configuration SSH en raison des permissions macOS.",
|
"sshConfigPermissionDenied": "Impossible d'accéder au fichier de configuration SSH en raison des permissions macOS.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferDiskAmount": "Prioritaskan tampilan kapasitas disk",
|
"preferDiskAmount": "Prioritaskan tampilan kapasitas disk",
|
||||||
"privateKey": "Kunci Pribadi",
|
"privateKey": "Kunci Pribadi",
|
||||||
|
"privateKeyNotFoundFmt": "Kunci privat [{keyId}] tidak ditemukan.",
|
||||||
"process": "Proses",
|
"process": "Proses",
|
||||||
"prune": "Pangkas",
|
"prune": "Pangkas",
|
||||||
"pushToken": "Dorong token",
|
"pushToken": "Dorong token",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "Apakah Anda ingin memberikan izin untuk membaca ~/.ssh/config dan secara otomatis mengimpor pengaturan server?",
|
"sshConfigImportPermission": "Apakah Anda ingin memberikan izin untuk membaca ~/.ssh/config dan secara otomatis mengimpor pengaturan server?",
|
||||||
"sshConfigImportTip": "Prompt untuk membaca ~/.ssh/config saat pembuatan server pertama",
|
"sshConfigImportTip": "Prompt untuk membaca ~/.ssh/config saat pembuatan server pertama",
|
||||||
"sshConfigImported": "Berhasil mengimpor {count} server dari konfigurasi SSH",
|
"sshConfigImported": "Berhasil mengimpor {count} server dari konfigurasi SSH",
|
||||||
|
"sshHostKeyChangedDesc": "Kunci host SSH untuk {serverName} telah berubah. Lanjutkan hanya jika Anda mempercayai server ini.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Sidik jari (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Sidik jari (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "Jenis kunci host SSH",
|
||||||
|
"sshHostKeyNewDesc": "Kunci host SSH baru diterima dari {serverName}. Periksa sidik jarinya sebelum mempercayai.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Sidik jari tersimpan: {fingerprint}",
|
||||||
"sshConfigManualSelect": "Apakah Anda ingin memilih file konfigurasi SSH secara manual?",
|
"sshConfigManualSelect": "Apakah Anda ingin memilih file konfigurasi SSH secara manual?",
|
||||||
"sshConfigNoServers": "Tidak ada server yang ditemukan dalam konfigurasi SSH",
|
"sshConfigNoServers": "Tidak ada server yang ditemukan dalam konfigurasi SSH",
|
||||||
"sshConfigPermissionDenied": "Tidak dapat mengakses file konfigurasi SSH karena izin macOS.",
|
"sshConfigPermissionDenied": "Tidak dapat mengakses file konfigurasi SSH karena izin macOS.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "ポート",
|
"port": "ポート",
|
||||||
"preferDiskAmount": "ディスク容量を優先的に表示",
|
"preferDiskAmount": "ディスク容量を優先的に表示",
|
||||||
"privateKey": "秘密鍵",
|
"privateKey": "秘密鍵",
|
||||||
|
"privateKeyNotFoundFmt": "秘密鍵 [{keyId}] が見つかりません。",
|
||||||
"process": "プロセス",
|
"process": "プロセス",
|
||||||
"prune": "剪定する",
|
"prune": "剪定する",
|
||||||
"pushToken": "プッシュトークン",
|
"pushToken": "プッシュトークン",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "~/.ssh/configを読み取ってサーバー設定を自動的にインポートする権限を与えますか?",
|
"sshConfigImportPermission": "~/.ssh/configを読み取ってサーバー設定を自動的にインポートする権限を与えますか?",
|
||||||
"sshConfigImportTip": "初回サーバー作成時に~/.ssh/configの読み取りを促す",
|
"sshConfigImportTip": "初回サーバー作成時に~/.ssh/configの読み取りを促す",
|
||||||
"sshConfigImported": "SSH設定から{count}個のサーバーをインポートしました",
|
"sshConfigImported": "SSH設定から{count}個のサーバーをインポートしました",
|
||||||
|
"sshHostKeyChangedDesc": "{serverName} の SSH ホスト鍵が変更されました。このサーバーを信頼できる場合のみ続行してください。",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "フィンガープリント (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "フィンガープリント (MD5 16進): {fingerprint}",
|
||||||
|
"sshHostKeyType": "SSH ホストキーの種類",
|
||||||
|
"sshHostKeyNewDesc": "{serverName} から新しい SSH ホスト鍵を受信しました。信頼する前にフィンガープリントを確認してください。",
|
||||||
|
"sshHostKeyStoredFingerprint": "保存済みフィンガープリント: {fingerprint}",
|
||||||
"sshConfigManualSelect": "SSH設定ファイルを手動で選択しますか?",
|
"sshConfigManualSelect": "SSH設定ファイルを手動で選択しますか?",
|
||||||
"sshConfigNoServers": "SSH設定でサーバーが見つかりませんでした",
|
"sshConfigNoServers": "SSH設定でサーバーが見つかりませんでした",
|
||||||
"sshConfigPermissionDenied": "macOSの権限により、SSH設定ファイルにアクセスできません。",
|
"sshConfigPermissionDenied": "macOSの権限により、SSH設定ファイルにアクセスできません。",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Poort",
|
"port": "Poort",
|
||||||
"preferDiskAmount": "Geef de schijfcapaciteit prioriteit bij weergave",
|
"preferDiskAmount": "Geef de schijfcapaciteit prioriteit bij weergave",
|
||||||
"privateKey": "Privésleutel",
|
"privateKey": "Privésleutel",
|
||||||
|
"privateKeyNotFoundFmt": "Privésleutel [{keyId}] niet gevonden.",
|
||||||
"process": "Proces",
|
"process": "Proces",
|
||||||
"prune": "Snoeien",
|
"prune": "Snoeien",
|
||||||
"pushToken": "Push-token",
|
"pushToken": "Push-token",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "Wilt u toestemming geven om ~/.ssh/config te lezen en automatisch serverinstellingen te importeren?",
|
"sshConfigImportPermission": "Wilt u toestemming geven om ~/.ssh/config te lezen en automatisch serverinstellingen te importeren?",
|
||||||
"sshConfigImportTip": "Prompt om ~/.ssh/config te lezen bij het aanmaken van de eerste server",
|
"sshConfigImportTip": "Prompt om ~/.ssh/config te lezen bij het aanmaken van de eerste server",
|
||||||
"sshConfigImported": "{count} servers geïmporteerd uit SSH-configuratie",
|
"sshConfigImported": "{count} servers geïmporteerd uit SSH-configuratie",
|
||||||
|
"sshHostKeyChangedDesc": "De SSH-hostsleutel voor {serverName} is gewijzigd. Ga alleen verder als u deze server vertrouwt.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Vingerafdruk (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Vingerafdruk (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "Type SSH-hostsleutel",
|
||||||
|
"sshHostKeyNewDesc": "Er is een nieuwe SSH-hostsleutel ontvangen van {serverName}. Controleer de vingerafdruk voordat u vertrouwt.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Opgeslagen vingerafdruk: {fingerprint}",
|
||||||
"sshConfigManualSelect": "Wilt u het SSH-configuratiebestand handmatig selecteren?",
|
"sshConfigManualSelect": "Wilt u het SSH-configuratiebestand handmatig selecteren?",
|
||||||
"sshConfigNoServers": "Geen servers gevonden in SSH-configuratie",
|
"sshConfigNoServers": "Geen servers gevonden in SSH-configuratie",
|
||||||
"sshConfigPermissionDenied": "Kan geen toegang krijgen tot SSH-configuratiebestand vanwege macOS-rechten.",
|
"sshConfigPermissionDenied": "Kan geen toegang krijgen tot SSH-configuratiebestand vanwege macOS-rechten.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Porta",
|
"port": "Porta",
|
||||||
"preferDiskAmount": "Priorizar a exibição da capacidade do disco",
|
"preferDiskAmount": "Priorizar a exibição da capacidade do disco",
|
||||||
"privateKey": "Chave privada",
|
"privateKey": "Chave privada",
|
||||||
|
"privateKeyNotFoundFmt": "Chave privada [{keyId}] não encontrada.",
|
||||||
"process": "Processo",
|
"process": "Processo",
|
||||||
"prune": "Podar",
|
"prune": "Podar",
|
||||||
"pushToken": "Token de notificação push",
|
"pushToken": "Token de notificação push",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "Gostaria de dar permissão para ler ~/.ssh/config e importar automaticamente as configurações do servidor?",
|
"sshConfigImportPermission": "Gostaria de dar permissão para ler ~/.ssh/config e importar automaticamente as configurações do servidor?",
|
||||||
"sshConfigImportTip": "Sugestão para ler ~/.ssh/config na criação do primeiro servidor",
|
"sshConfigImportTip": "Sugestão para ler ~/.ssh/config na criação do primeiro servidor",
|
||||||
"sshConfigImported": "Importados {count} servidores da configuração SSH",
|
"sshConfigImported": "Importados {count} servidores da configuração SSH",
|
||||||
|
"sshHostKeyChangedDesc": "A chave de host SSH de {serverName} foi alterada. Continue apenas se confiar neste servidor.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Impressão digital (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Impressão digital (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "Tipo de chave de host SSH",
|
||||||
|
"sshHostKeyNewDesc": "Uma nova chave de host SSH foi recebida de {serverName}. Verifique a impressão digital antes de confiar.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Impressão digital armazenada: {fingerprint}",
|
||||||
"sshConfigManualSelect": "Gostaria de selecionar manualmente o arquivo de configuração SSH?",
|
"sshConfigManualSelect": "Gostaria de selecionar manualmente o arquivo de configuração SSH?",
|
||||||
"sshConfigNoServers": "Nenhum servidor encontrado na configuração SSH",
|
"sshConfigNoServers": "Nenhum servidor encontrado na configuração SSH",
|
||||||
"sshConfigPermissionDenied": "Não é possível acessar o arquivo de configuração SSH devido às permissões do macOS.",
|
"sshConfigPermissionDenied": "Não é possível acessar o arquivo de configuração SSH devido às permissões do macOS.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Порт",
|
"port": "Порт",
|
||||||
"preferDiskAmount": "Приоритетное отображение объёма диска",
|
"preferDiskAmount": "Приоритетное отображение объёма диска",
|
||||||
"privateKey": "Приватный ключ",
|
"privateKey": "Приватный ключ",
|
||||||
|
"privateKeyNotFoundFmt": "Закрытый ключ [{keyId}] не найден.",
|
||||||
"process": "Процесс",
|
"process": "Процесс",
|
||||||
"prune": "Обрезать",
|
"prune": "Обрезать",
|
||||||
"pushToken": "Токен уведомлений",
|
"pushToken": "Токен уведомлений",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "Хотите ли вы дать разрешение на чтение ~/.ssh/config и автоматический импорт настроек сервера?",
|
"sshConfigImportPermission": "Хотите ли вы дать разрешение на чтение ~/.ssh/config и автоматический импорт настроек сервера?",
|
||||||
"sshConfigImportTip": "Предложение прочитать ~/.ssh/config при создании первого сервера",
|
"sshConfigImportTip": "Предложение прочитать ~/.ssh/config при создании первого сервера",
|
||||||
"sshConfigImported": "Импортировано {count} серверов из SSH-конфигурации",
|
"sshConfigImported": "Импортировано {count} серверов из SSH-конфигурации",
|
||||||
|
"sshHostKeyChangedDesc": "SSH-ключ хоста для {serverName} изменился. Продолжайте только если доверяете этому серверу.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Отпечаток (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Отпечаток (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "Тип ключа хоста SSH",
|
||||||
|
"sshHostKeyNewDesc": "Получен новый SSH-ключ хоста от {serverName}. Проверьте отпечаток перед продолжением.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Сохранённый отпечаток: {fingerprint}",
|
||||||
"sshConfigManualSelect": "Хотели бы вы вручную выбрать файл конфигурации SSH?",
|
"sshConfigManualSelect": "Хотели бы вы вручную выбрать файл конфигурации SSH?",
|
||||||
"sshConfigNoServers": "Серверы не найдены в SSH-конфигурации",
|
"sshConfigNoServers": "Серверы не найдены в SSH-конфигурации",
|
||||||
"sshConfigPermissionDenied": "Невозможно получить доступ к файлу конфигурации SSH из-за разрешений macOS.",
|
"sshConfigPermissionDenied": "Невозможно получить доступ к файлу конфигурации SSH из-за разрешений macOS.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferDiskAmount": "Disk kapasitesini öncelikli olarak göster",
|
"preferDiskAmount": "Disk kapasitesini öncelikli olarak göster",
|
||||||
"privateKey": "Özel Anahtar",
|
"privateKey": "Özel Anahtar",
|
||||||
|
"privateKeyNotFoundFmt": "Özel anahtar [{keyId}] bulunamadı.",
|
||||||
"process": "İşlem",
|
"process": "İşlem",
|
||||||
"prune": "Budamak",
|
"prune": "Budamak",
|
||||||
"pushToken": "Push belirteci",
|
"pushToken": "Push belirteci",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "~/.ssh/config dosyasını okumak ve sunucu ayarlarını otomatik olarak içe aktarmak için izin vermek ister misiniz?",
|
"sshConfigImportPermission": "~/.ssh/config dosyasını okumak ve sunucu ayarlarını otomatik olarak içe aktarmak için izin vermek ister misiniz?",
|
||||||
"sshConfigImportTip": "İlk sunucu oluşturulurken ~/.ssh/config okuma istemi",
|
"sshConfigImportTip": "İlk sunucu oluşturulurken ~/.ssh/config okuma istemi",
|
||||||
"sshConfigImported": "SSH yapılandırmasından {count} sunucu içe aktarıldı",
|
"sshConfigImported": "SSH yapılandırmasından {count} sunucu içe aktarıldı",
|
||||||
|
"sshHostKeyChangedDesc": "{serverName} için SSH ana bilgisayar anahtarı değişti. Yalnızca bu sunucuya güveniyorsanız devam edin.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Parmak izi (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Parmak izi (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "SSH ana bilgisayar anahtarı türü",
|
||||||
|
"sshHostKeyNewDesc": "{serverName} üzerinden yeni bir SSH ana bilgisayar anahtarı alındı. Güvenmeden önce parmak izini kontrol edin.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Kaydedilen parmak izi: {fingerprint}",
|
||||||
"sshConfigManualSelect": "SSH yapılandırma dosyasını manuel olarak seçmek ister misiniz?",
|
"sshConfigManualSelect": "SSH yapılandırma dosyasını manuel olarak seçmek ister misiniz?",
|
||||||
"sshConfigNoServers": "SSH yapılandırmasında sunucu bulunamadı",
|
"sshConfigNoServers": "SSH yapılandırmasında sunucu bulunamadı",
|
||||||
"sshConfigPermissionDenied": "macOS izinleri nedeniyle SSH yapılandırma dosyasına erişilemiyor.",
|
"sshConfigPermissionDenied": "macOS izinleri nedeniyle SSH yapılandırma dosyasına erişilemiyor.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "Порт",
|
"port": "Порт",
|
||||||
"preferDiskAmount": "Пріоритетно показувати ємність диска",
|
"preferDiskAmount": "Пріоритетно показувати ємність диска",
|
||||||
"privateKey": "Приватний ключ",
|
"privateKey": "Приватний ключ",
|
||||||
|
"privateKeyNotFoundFmt": "Приватний ключ [{keyId}] не знайдено.",
|
||||||
"process": "Процес",
|
"process": "Процес",
|
||||||
"prune": "Обрізати",
|
"prune": "Обрізати",
|
||||||
"pushToken": "Надіслати токен",
|
"pushToken": "Надіслати токен",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "Чи хочете ви надати дозвіл на читання ~/.ssh/config та автоматичний імпорт налаштувань сервера?",
|
"sshConfigImportPermission": "Чи хочете ви надати дозвіл на читання ~/.ssh/config та автоматичний імпорт налаштувань сервера?",
|
||||||
"sshConfigImportTip": "Пропозиція прочитати ~/.ssh/config при створенні першого сервера",
|
"sshConfigImportTip": "Пропозиція прочитати ~/.ssh/config при створенні першого сервера",
|
||||||
"sshConfigImported": "Імпортовано {count} серверів з SSH-конфігурації",
|
"sshConfigImported": "Імпортовано {count} серверів з SSH-конфігурації",
|
||||||
|
"sshHostKeyChangedDesc": "SSH-ключ хоста для {serverName} змінено. Продовжуйте лише якщо довіряєте цьому серверу.",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "Відбиток (MD5 Base64): {fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "Відбиток (MD5 hex): {fingerprint}",
|
||||||
|
"sshHostKeyType": "Тип ключа хоста SSH",
|
||||||
|
"sshHostKeyNewDesc": "Отримано новий SSH-ключ хоста від {serverName}. Перевірте відбиток перед тим, як довіряти.",
|
||||||
|
"sshHostKeyStoredFingerprint": "Збережений відбиток: {fingerprint}",
|
||||||
"sshConfigManualSelect": "Чи хочете ви вручну вибрати файл конфігурації SSH?",
|
"sshConfigManualSelect": "Чи хочете ви вручну вибрати файл конфігурації SSH?",
|
||||||
"sshConfigNoServers": "Сервери не знайдені в SSH-конфігурації",
|
"sshConfigNoServers": "Сервери не знайдені в SSH-конфігурації",
|
||||||
"sshConfigPermissionDenied": "Неможливо отримати доступ до файлу конфігурації SSH через дозволи macOS.",
|
"sshConfigPermissionDenied": "Неможливо отримати доступ до файлу конфігурації SSH через дозволи macOS.",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "端口",
|
"port": "端口",
|
||||||
"preferDiskAmount": "优先显示硬盘容量",
|
"preferDiskAmount": "优先显示硬盘容量",
|
||||||
"privateKey": "私钥",
|
"privateKey": "私钥",
|
||||||
|
"privateKeyNotFoundFmt": "未找到私钥 [{keyId}]。",
|
||||||
"process": "进程",
|
"process": "进程",
|
||||||
"prune": "修剪",
|
"prune": "修剪",
|
||||||
"pushToken": "消息推送 Token",
|
"pushToken": "消息推送 Token",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "是否允许读取 ~/.ssh/config 并自动导入服务器设置?",
|
"sshConfigImportPermission": "是否允许读取 ~/.ssh/config 并自动导入服务器设置?",
|
||||||
"sshConfigImportTip": "首次创建服务器时提示读取 ~/.ssh/config",
|
"sshConfigImportTip": "首次创建服务器时提示读取 ~/.ssh/config",
|
||||||
"sshConfigImported": "从 SSH 配置导入了 {count} 个服务器",
|
"sshConfigImported": "从 SSH 配置导入了 {count} 个服务器",
|
||||||
|
"sshHostKeyChangedDesc": "服务器 {serverName} 的 SSH 主机密钥已更改,仅在信任该服务器时继续。",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "指纹(MD5 Base64):{fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "指纹(MD5 十六进制):{fingerprint}",
|
||||||
|
"sshHostKeyType": "SSH 主机密钥类型",
|
||||||
|
"sshHostKeyNewDesc": "收到来自 {serverName} 的新 SSH 主机密钥,在信任前请检查指纹。",
|
||||||
|
"sshHostKeyStoredFingerprint": "已存储的指纹:{fingerprint}",
|
||||||
"sshConfigManualSelect": "是否要手动选择 SSH 配置文件?",
|
"sshConfigManualSelect": "是否要手动选择 SSH 配置文件?",
|
||||||
"sshConfigNoServers": "SSH 配置中未找到服务器",
|
"sshConfigNoServers": "SSH 配置中未找到服务器",
|
||||||
"sshConfigPermissionDenied": "由于 macOS 权限限制,无法访问 SSH 配置文件。",
|
"sshConfigPermissionDenied": "由于 macOS 权限限制,无法访问 SSH 配置文件。",
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"port": "埠",
|
"port": "埠",
|
||||||
"preferDiskAmount": "優先顯示硬碟容量",
|
"preferDiskAmount": "優先顯示硬碟容量",
|
||||||
"privateKey": "私鑰",
|
"privateKey": "私鑰",
|
||||||
|
"privateKeyNotFoundFmt": "未找到私鑰 [{keyId}]。",
|
||||||
"process": "處理程序",
|
"process": "處理程序",
|
||||||
"prune": "修剪",
|
"prune": "修剪",
|
||||||
"pushToken": "消息推送 Token",
|
"pushToken": "消息推送 Token",
|
||||||
@@ -225,6 +226,12 @@
|
|||||||
"sshConfigImportPermission": "您是否希望允許讀取 ~/.ssh/config 並自動匯入伺服器設定?",
|
"sshConfigImportPermission": "您是否希望允許讀取 ~/.ssh/config 並自動匯入伺服器設定?",
|
||||||
"sshConfigImportTip": "在建立第一個伺服器時提示讀取 ~/.ssh/config",
|
"sshConfigImportTip": "在建立第一個伺服器時提示讀取 ~/.ssh/config",
|
||||||
"sshConfigImported": "已從SSH設定匯入{count}個伺服器",
|
"sshConfigImported": "已從SSH設定匯入{count}個伺服器",
|
||||||
|
"sshHostKeyChangedDesc": "伺服器 {serverName} 的 SSH 主機金鑰已變更,僅在信任該伺服器時繼續。",
|
||||||
|
"sshHostKeyFingerprintMd5Base64": "指紋(MD5 Base64):{fingerprint}",
|
||||||
|
"sshHostKeyFingerprintMd5Hex": "指紋(MD5 十六進位):{fingerprint}",
|
||||||
|
"sshHostKeyType": "SSH 主機金鑰類型",
|
||||||
|
"sshHostKeyNewDesc": "收到來自 {serverName} 的新 SSH 主機金鑰,信任前請先檢查指紋。",
|
||||||
|
"sshHostKeyStoredFingerprint": "已儲存的指紋:{fingerprint}",
|
||||||
"sshConfigManualSelect": "是否要手動選擇 SSH 設定檔案?",
|
"sshConfigManualSelect": "是否要手動選擇 SSH 設定檔案?",
|
||||||
"sshConfigNoServers": "SSH設定中未找到伺服器",
|
"sshConfigNoServers": "SSH設定中未找到伺服器",
|
||||||
"sshConfigPermissionDenied": "由於 macOS 權限限制,無法存取 SSH 設定檔案。",
|
"sshConfigPermissionDenied": "由於 macOS 權限限制,無法存取 SSH 設定檔案。",
|
||||||
|
|||||||
@@ -41,11 +41,7 @@ class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
|
|||||||
appBar: CustomAppBar(
|
appBar: CustomAppBar(
|
||||||
title: Text(l10n.connectionStats),
|
title: Text(l10n.connectionStats),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(onPressed: _loadStats, icon: const Icon(Icons.refresh), tooltip: libL10n.refresh),
|
||||||
onPressed: _loadStats,
|
|
||||||
icon: const Icon(Icons.refresh),
|
|
||||||
tooltip: libL10n.refresh,
|
|
||||||
),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: _showClearAllDialog,
|
onPressed: _showClearAllDialog,
|
||||||
icon: const Icon(Icons.clear_all, color: Colors.red),
|
icon: const Icon(Icons.clear_all, color: Colors.red),
|
||||||
@@ -75,140 +71,90 @@ class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildServerStatsCard(ServerConnectionStats stats) {
|
Widget _buildServerStatsCard(ServerConnectionStats stats) {
|
||||||
final successRate = stats.totalAttempts == 0
|
final successRate = stats.totalAttempts == 0 ? 'N/A' : '${(stats.successRate * 100).toStringAsFixed(1)}%';
|
||||||
? 'N/A'
|
|
||||||
: '${(stats.successRate * 100).toStringAsFixed(1)}%';
|
|
||||||
final lastSuccessTime = stats.lastSuccessTime;
|
final lastSuccessTime = stats.lastSuccessTime;
|
||||||
final lastFailureTime = stats.lastFailureTime;
|
final lastFailureTime = stats.lastFailureTime;
|
||||||
|
|
||||||
return Card(
|
return Padding(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: const EdgeInsets.all(16),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Expanded(
|
||||||
children: [
|
child: Text(
|
||||||
Expanded(
|
stats.serverName,
|
||||||
child: Text(
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
stats.serverName,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
'${libL10n.success}: $successRate',
|
Text(
|
||||||
style: TextStyle(
|
'${libL10n.success}: $successRate',
|
||||||
fontSize: 16,
|
style: TextStyle(
|
||||||
color: stats.successRate >= 0.8
|
fontSize: 16,
|
||||||
? Colors.green
|
color: stats.successRate >= 0.8
|
||||||
: stats.successRate >= 0.5
|
? Colors.green
|
||||||
? Colors.orange
|
: stats.successRate >= 0.5
|
||||||
: Colors.red,
|
? Colors.orange
|
||||||
fontWeight: FontWeight.bold,
|
: Colors.red,
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
_buildStatItem(
|
|
||||||
l10n.totalAttempts,
|
|
||||||
stats.totalAttempts.toString(),
|
|
||||||
Icons.all_inclusive,
|
|
||||||
),
|
|
||||||
_buildStatItem(
|
|
||||||
libL10n.success,
|
|
||||||
stats.successCount.toString(),
|
|
||||||
Icons.check_circle,
|
|
||||||
Colors.green,
|
|
||||||
),
|
|
||||||
_buildStatItem(
|
|
||||||
libL10n.fail,
|
|
||||||
stats.failureCount.toString(),
|
|
||||||
Icons.error,
|
|
||||||
Colors.red,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (lastSuccessTime != null || lastFailureTime != null) ...[
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
if (lastSuccessTime != null)
|
|
||||||
_buildTimeItem(
|
|
||||||
l10n.lastSuccess,
|
|
||||||
lastSuccessTime,
|
|
||||||
Icons.check_circle,
|
|
||||||
Colors.green,
|
|
||||||
),
|
|
||||||
if (lastFailureTime != null)
|
|
||||||
_buildTimeItem(
|
|
||||||
l10n.lastFailure,
|
|
||||||
lastFailureTime,
|
|
||||||
Icons.error,
|
|
||||||
Colors.red,
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildStatItem(l10n.totalAttempts, stats.totalAttempts.toString(), Icons.all_inclusive),
|
||||||
|
_buildStatItem(
|
||||||
|
libL10n.success,
|
||||||
|
stats.successCount.toString(),
|
||||||
|
Icons.check_circle,
|
||||||
|
Colors.green,
|
||||||
|
),
|
||||||
|
_buildStatItem(libL10n.fail, stats.failureCount.toString(), Icons.error, Colors.red),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (lastSuccessTime != null || lastFailureTime != null) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
const Divider(),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
l10n.recentConnections,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => _showServerDetailsDialog(stats),
|
|
||||||
child: Text(l10n.viewDetails),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
...stats.recentConnections.take(3).map(_buildConnectionItem),
|
if (lastSuccessTime != null)
|
||||||
|
_buildTimeItem(l10n.lastSuccess, lastSuccessTime, Icons.check_circle, Colors.green),
|
||||||
|
if (lastFailureTime != null)
|
||||||
|
_buildTimeItem(l10n.lastFailure, lastFailureTime, Icons.error, Colors.red),
|
||||||
],
|
],
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(l10n.recentConnections, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||||
|
TextButton(onPressed: () => _showServerDetailsDialog(stats), child: Text(l10n.viewDetails)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
...stats.recentConnections.take(3).map(_buildConnectionItem),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
).cardx;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStatItem(
|
Widget _buildStatItem(String label, String value, IconData icon, [Color? color]) {
|
||||||
String label,
|
|
||||||
String value,
|
|
||||||
IconData icon, [
|
|
||||||
Color? color,
|
|
||||||
]) {
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, size: 24, color: color ?? Colors.grey),
|
Icon(icon, size: 24, color: color ?? Colors.grey),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: color),
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(label, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
Text(label, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTimeItem(
|
Widget _buildTimeItem(String label, DateTime time, IconData icon, Color color) {
|
||||||
String label,
|
|
||||||
DateTime time,
|
|
||||||
IconData icon,
|
|
||||||
Color color,
|
|
||||||
) {
|
|
||||||
final timeStr = time.simple();
|
final timeStr = time.simple();
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
@@ -216,10 +162,7 @@ class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(icon, size: 16, color: color),
|
Icon(icon, size: 16, color: color),
|
||||||
UIs.width7,
|
UIs.width7,
|
||||||
Text(
|
Text('$label: ', style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
||||||
'$label: ',
|
|
||||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
|
||||||
),
|
|
||||||
Text(timeStr, style: const TextStyle(fontSize: 12)),
|
Text(timeStr, style: const TextStyle(fontSize: 12)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -244,13 +187,8 @@ class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
|
|||||||
UIs.width7,
|
UIs.width7,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
isSuccess
|
isSuccess ? '${libL10n.success} (${stat.durationMs}ms)' : stat.result.displayName,
|
||||||
? '${libL10n.success} (${stat.durationMs}ms)'
|
style: TextStyle(fontSize: 12, color: isSuccess ? Colors.green : Colors.red),
|
||||||
: stat.result.displayName,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: isSuccess ? Colors.green : Colors.red,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -289,9 +227,7 @@ extension on _ConnectionStatsPageState {
|
|||||||
isSuccess
|
isSuccess
|
||||||
? '${libL10n.success} (${stat.durationMs}ms)'
|
? '${libL10n.success} (${stat.durationMs}ms)'
|
||||||
: '${libL10n.fail}: ${stat.result.displayName}',
|
: '${libL10n.fail}: ${stat.result.displayName}',
|
||||||
style: TextStyle(
|
style: TextStyle(color: isSuccess ? Colors.green : Colors.red),
|
||||||
color: isSuccess ? Colors.green : Colors.red,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (!isSuccess && stat.errorMessage.isNotEmpty)
|
if (!isSuccess && stat.errorMessage.isNotEmpty)
|
||||||
Text(
|
Text(
|
||||||
@@ -313,10 +249,7 @@ extension on _ConnectionStatsPageState {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
_showClearServerStatsDialog(stats);
|
_showClearServerStatsDialog(stats);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(l10n.clearThisServerStats, style: TextStyle(color: Colors.red)),
|
||||||
l10n.clearThisServerStats,
|
|
||||||
style: TextStyle(color: Colors.red),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -284,4 +284,122 @@ extension _App on _AppSettingsPageState {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildEditRawSettings() {
|
||||||
|
return ListTile(
|
||||||
|
title: const Text('(Dev) Edit raw json'),
|
||||||
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||||
|
onTap: _editRawSettings,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _editRawSettings() async {
|
||||||
|
final rawMap = Stores.setting.getAllMap(includeInternalKeys: true);
|
||||||
|
final map = Map<String, Object?>.from(rawMap);
|
||||||
|
final initialKeys = Set<String>.from(map.keys);
|
||||||
|
Map<String, Object?> mapForEditor = map;
|
||||||
|
String? encryptedKey;
|
||||||
|
String? passwordUsed;
|
||||||
|
|
||||||
|
Future<String?> resolvePassword() async {
|
||||||
|
final saved = await _setting.backupasswd.read();
|
||||||
|
if (saved?.isNotEmpty == true) return saved;
|
||||||
|
final backupPwd = await SecureStoreProps.bakPwd.read();
|
||||||
|
if (backupPwd?.isNotEmpty == true) return backupPwd;
|
||||||
|
final controller = TextEditingController();
|
||||||
|
try {
|
||||||
|
final result = await context.showRoundDialog<String>(
|
||||||
|
title: libL10n.pwd,
|
||||||
|
child: Input(
|
||||||
|
controller: controller,
|
||||||
|
label: libL10n.pwd,
|
||||||
|
obscureText: true,
|
||||||
|
onSubmitted: (_) => context.pop(controller.text.trim()),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => context.pop(null), child: Text(libL10n.cancel)),
|
||||||
|
TextButton(onPressed: () => context.pop(controller.text.trim()), child: Text(libL10n.ok)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return result?.trim();
|
||||||
|
} finally {
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final entry in map.entries) {
|
||||||
|
final value = entry.value;
|
||||||
|
if (value is String && Cryptor.isEncrypted(value)) {
|
||||||
|
final password = await resolvePassword();
|
||||||
|
if (password == null || password.isEmpty) {
|
||||||
|
context.showSnackBar(libL10n.cancel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final decrypted = Cryptor.decrypt(value, password);
|
||||||
|
final decoded = json.decode(decrypted);
|
||||||
|
if (decoded is Map<String, dynamic>) {
|
||||||
|
mapForEditor = Map<String, Object?>.from(decoded);
|
||||||
|
encryptedKey = entry.key;
|
||||||
|
passwordUsed = password;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
context.showRoundDialog(title: libL10n.fail, child: Text(l10n.invalid));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e, stack) {
|
||||||
|
final msg = e.toString().contains('Failed to decrypt') || e.toString().contains('incorrect password')
|
||||||
|
? l10n.backupPasswordWrong
|
||||||
|
: '${libL10n.error}:\n$e';
|
||||||
|
context.showRoundDialog(title: libL10n.fail, child: Text(msg));
|
||||||
|
Loggers.app.warning('Decrypt raw settings failed', e, stack);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSave(EditorPageRet ret) {
|
||||||
|
if (ret.typ != EditorPageRetType.text) {
|
||||||
|
context.showRoundDialog(title: libL10n.fail, child: Text(l10n.invalid));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final newSettings = json.decode(ret.val) as Map<String, dynamic>;
|
||||||
|
if (encryptedKey != null) {
|
||||||
|
final pwd = passwordUsed;
|
||||||
|
if (pwd == null || pwd.isEmpty) {
|
||||||
|
context.showRoundDialog(title: libL10n.fail, child: Text(l10n.invalid));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final encrypted = Cryptor.encrypt(json.encode(newSettings), pwd);
|
||||||
|
Stores.setting.box.put(encryptedKey, encrypted);
|
||||||
|
} else {
|
||||||
|
Stores.setting.box.putAll(newSettings);
|
||||||
|
final newKeys = newSettings.keys.toSet();
|
||||||
|
final removedKeys = initialKeys.where((e) => !newKeys.contains(e));
|
||||||
|
for (final key in removedKeys) {
|
||||||
|
Stores.setting.box.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e, trace) {
|
||||||
|
context.showRoundDialog(title: libL10n.error, child: Text('${l10n.save}:\n$e'));
|
||||||
|
Loggers.app.warning('Update json settings failed', e, trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode [map] to String with indent `\t`
|
||||||
|
final text = jsonIndentEncoder.convert(mapForEditor);
|
||||||
|
await EditorPage.route.go(
|
||||||
|
context,
|
||||||
|
args: EditorPageArgs(
|
||||||
|
text: text,
|
||||||
|
lang: ProgLang.json,
|
||||||
|
title: libL10n.setting,
|
||||||
|
onSave: onSave,
|
||||||
|
closeAfterSave: SettingStore.instance.closeAfterSave.fetch(),
|
||||||
|
softWrap: SettingStore.instance.editorSoftWrap.fetch(),
|
||||||
|
enableHighlight: SettingStore.instance.editorHighlight.fetch(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,10 +83,10 @@ class _HomeTabsConfigPageState extends ConsumerState<HomeTabsConfigPage> {
|
|||||||
onTap: isSelected && canRemove ? () => _removeTab(tab) : null,
|
onTap: isSelected && canRemove ? () => _removeTab(tab) : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
return Card(
|
return Padding(
|
||||||
key: ValueKey(tab.name),
|
key: ValueKey(tab.name),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
|
||||||
child: isSelected ? ReorderableDragStartListener(index: index, child: child) : child,
|
child: (isSelected ? ReorderableDragStartListener(index: index, child: child) : child).cardx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -207,53 +207,6 @@ extension _Server on _AppSettingsPageState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEditRawSettings() {
|
|
||||||
return ListTile(
|
|
||||||
title: const Text('(Dev) Edit raw json'),
|
|
||||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
|
||||||
onTap: _editRawSettings,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _editRawSettings() async {
|
|
||||||
final map = Stores.setting.getAllMap(includeInternalKeys: true);
|
|
||||||
final keys = map.keys;
|
|
||||||
|
|
||||||
void onSave(EditorPageRet ret) {
|
|
||||||
if (ret.typ != EditorPageRetType.text) {
|
|
||||||
context.showRoundDialog(title: libL10n.fail, child: Text(l10n.invalid));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final newSettings = json.decode(ret.val) as Map<String, dynamic>;
|
|
||||||
Stores.setting.box.putAll(newSettings);
|
|
||||||
final newKeys = newSettings.keys;
|
|
||||||
final removedKeys = keys.where((e) => !newKeys.contains(e));
|
|
||||||
for (final key in removedKeys) {
|
|
||||||
Stores.setting.box.delete(key);
|
|
||||||
}
|
|
||||||
} catch (e, trace) {
|
|
||||||
context.showRoundDialog(title: libL10n.error, child: Text('${l10n.save}:\n$e'));
|
|
||||||
Loggers.app.warning('Update json settings failed', e, trace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode [map] to String with indent `\t`
|
|
||||||
final text = jsonIndentEncoder.convert(map);
|
|
||||||
await EditorPage.route.go(
|
|
||||||
context,
|
|
||||||
args: EditorPageArgs(
|
|
||||||
text: text,
|
|
||||||
lang: ProgLang.json,
|
|
||||||
title: libL10n.setting,
|
|
||||||
onSave: onSave,
|
|
||||||
closeAfterSave: SettingStore.instance.closeAfterSave.fetch(),
|
|
||||||
softWrap: SettingStore.instance.editorSoftWrap.fetch(),
|
|
||||||
enableHighlight: SettingStore.instance.editorHighlight.fetch(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildCpuView() {
|
Widget _buildCpuView() {
|
||||||
return ExpandTile(
|
return ExpandTile(
|
||||||
leading: const Icon(OctIcons.cpu, size: _kIconSize),
|
leading: const Icon(OctIcons.cpu, size: _kIconSize),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
import 'package:server_box/core/utils/host_key_helper.dart';
|
||||||
import 'package:server_box/data/model/app/path_with_prefix.dart';
|
import 'package:server_box/data/model/app/path_with_prefix.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';
|
||||||
@@ -370,6 +371,10 @@ extension _OnTapFile on _LocalFilePageState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await ensureHostKeyAcceptedForSftp(context, spi)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ref.read(sftpProvider.notifier).add(SftpReq(spi, '$remotePath/$fileName', file.absolute.path, SftpReqType.upload));
|
ref.read(sftpProvider.notifier).add(SftpReq(spi, '$remotePath/$fileName', file.absolute.path, SftpReqType.upload));
|
||||||
context.showSnackBar(l10n.added2List);
|
context.showSnackBar(l10n.added2List);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:icons_plus/icons_plus.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/sftpfile.dart';
|
import 'package:server_box/core/extension/sftpfile.dart';
|
||||||
import 'package:server_box/core/utils/comparator.dart';
|
import 'package:server_box/core/utils/comparator.dart';
|
||||||
|
import 'package:server_box/core/utils/host_key_helper.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';
|
||||||
@@ -286,6 +287,10 @@ extension _Actions on _SftpPageState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await ensureHostKeyAcceptedForSftp(context, widget.args.spi)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final remotePath = _getRemotePath(name);
|
final remotePath = _getRemotePath(name);
|
||||||
final localPath = _getLocalPath(remotePath);
|
final localPath = _getLocalPath(remotePath);
|
||||||
final completer = Completer();
|
final completer = Completer();
|
||||||
@@ -298,7 +303,10 @@ extension _Actions on _SftpPageState {
|
|||||||
context,
|
context,
|
||||||
args: EditorPageArgs(
|
args: EditorPageArgs(
|
||||||
path: localPath,
|
path: localPath,
|
||||||
onSave: (_) {
|
onSave: (_) async {
|
||||||
|
if (!await ensureHostKeyAcceptedForSftp(context, req.spi)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ref
|
ref
|
||||||
.read(sftpProvider.notifier)
|
.read(sftpProvider.notifier)
|
||||||
.add(SftpReq(req.spi, remotePath, localPath, SftpReqType.upload));
|
.add(SftpReq(req.spi, remotePath, localPath, SftpReqType.upload));
|
||||||
@@ -322,6 +330,10 @@ extension _Actions on _SftpPageState {
|
|||||||
context.pop();
|
context.pop();
|
||||||
final remotePath = _getRemotePath(name);
|
final remotePath = _getRemotePath(name);
|
||||||
|
|
||||||
|
if (!await ensureHostKeyAcceptedForSftp(context, widget.args.spi)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ref
|
ref
|
||||||
.read(sftpProvider.notifier)
|
.read(sftpProvider.notifier)
|
||||||
.add(SftpReq(widget.args.spi, remotePath, _getLocalPath(remotePath), SftpReqType.download));
|
.add(SftpReq(widget.args.spi, remotePath, _getLocalPath(remotePath), SftpReqType.download));
|
||||||
@@ -652,6 +664,9 @@ extension _Actions on _SftpPageState {
|
|||||||
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');
|
||||||
|
if (!await ensureHostKeyAcceptedForSftp(context, widget.args.spi)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ref
|
ref
|
||||||
.read(sftpProvider.notifier)
|
.read(sftpProvider.notifier)
|
||||||
.add(SftpReq(widget.args.spi, remotePath, path, SftpReqType.upload));
|
.add(SftpReq(widget.args.spi, remotePath, path, SftpReqType.upload));
|
||||||
|
|||||||
Reference in New Issue
Block a user