mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-08-25 07:36:41 +00:00
refact(oidc): manually open the browser (#15706)
* refact(oidc): manually open the browser Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): allow copying OIDC authentication links Signed-off-by: fufesou <linlong1266@gmail.com> * Remove unused translation in ko.rs Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): better hint on browser didn't open Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): login handle exception Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): remove unused translations Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): login handle error Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): login in flight Signed-off-by: fufesou <linlong1266@gmail.com> * refact(translation): move "Continue" to the end of template.rs Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): var rename Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): remove useless "open sign-in page" Signed-off-by: fufesou <linlong1266@gmail.com> * Remove unecessary translation contents Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): better way to show&expand the url Signed-off-by: fufesou <linlong1266@gmail.com> * refact(oidc): better login ui Signed-off-by: fufesou <linlong1266@gmail.com> * fix(oidc): discard stale auth results after cancellation Signed-off-by: fufesou <linlong1266@gmail.com> * fix(oidc): handle auth status query failures safely Signed-off-by: fufesou <linlong1266@gmail.com> * fix(oidc): prevent concurrent login operations - reuse the active login dialog and block duplicate password submissions - cancel only active OIDC operations when closing the dialog - preserve authentication state until failure cancellation succeeds Signed-off-by: fufesou <linlong1266@gmail.com> * fix(oidc): refine login options error feedback Preserve typed errors to hide the network tip for HTTP failures and clarify the login-options API contract. Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/models/user_model.dart';
|
||||
@@ -11,6 +12,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
import './dialog.dart';
|
||||
import './oidc_auth_status.dart';
|
||||
|
||||
const kOpSvgList = [
|
||||
'github',
|
||||
@@ -23,6 +25,8 @@ const kOpSvgList = [
|
||||
'auth0',
|
||||
'microsoft'
|
||||
];
|
||||
const _requestingAccountAuth = 'Requesting account auth';
|
||||
const _waitingAccountAuth = 'Waiting account auth';
|
||||
|
||||
class _OidcProviderBranding {
|
||||
final String label;
|
||||
@@ -90,6 +94,7 @@ class ButtonOP extends StatelessWidget {
|
||||
final Color primaryColor;
|
||||
final double height;
|
||||
final Function() onTap;
|
||||
final bool Function() canStartAuth;
|
||||
|
||||
const ButtonOP({
|
||||
Key? key,
|
||||
@@ -99,6 +104,7 @@ class ButtonOP extends StatelessWidget {
|
||||
required this.primaryColor,
|
||||
required this.height,
|
||||
required this.onTap,
|
||||
required this.canStartAuth,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -111,11 +117,10 @@ class ButtonOP extends StatelessWidget {
|
||||
width: 200,
|
||||
child: Obx(() => ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: curOP.value.isEmpty || curOP.value == op
|
||||
? primaryColor
|
||||
: Colors.grey,
|
||||
backgroundColor: primaryColor,
|
||||
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
|
||||
onPressed: curOP.value.isEmpty || curOP.value == op ? onTap : null,
|
||||
onPressed:
|
||||
curOP.value == 'rustdesk' || !canStartAuth() ? null : onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
@@ -145,15 +150,120 @@ class ConfigOP {
|
||||
ConfigOP({required this.op, required this.icon});
|
||||
}
|
||||
|
||||
class _OidcAuthController {
|
||||
final RxString curOP = ''.obs;
|
||||
Future<void> _pendingOperation = Future<void>.value();
|
||||
int _authAttempt = 0;
|
||||
bool _closed = false;
|
||||
final _cancelInProgress = false.obs;
|
||||
|
||||
bool _isCurrent(int authAttempt, String op) {
|
||||
return !_closed && authAttempt == _authAttempt && curOP.value == op;
|
||||
}
|
||||
|
||||
Future<bool> start(String op) {
|
||||
if (!canStart()) {
|
||||
return Future<bool>.value(false);
|
||||
}
|
||||
final authAttempt = ++_authAttempt;
|
||||
curOP.value = op;
|
||||
// Web auth must start during the original user gesture so popups are allowed.
|
||||
if (isWeb) {
|
||||
return _startWeb(authAttempt, op);
|
||||
}
|
||||
final completer = Completer<bool>();
|
||||
_pendingOperation = _pendingOperation.then((_) async {
|
||||
if (!_isCurrent(authAttempt, op)) {
|
||||
completer.complete(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await bind.mainAccountAuthCancel();
|
||||
if (!_isCurrent(authAttempt, op)) {
|
||||
completer.complete(false);
|
||||
return;
|
||||
}
|
||||
await bind.mainAccountAuth(op: op, rememberMe: true);
|
||||
completer.complete(_isCurrent(authAttempt, op));
|
||||
} catch (error, stackTrace) {
|
||||
completer.completeError(error, stackTrace);
|
||||
}
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<bool> _startWeb(int authAttempt, String op) async {
|
||||
await bind.mainAccountAuth(op: op, rememberMe: true);
|
||||
return _isCurrent(authAttempt, op);
|
||||
}
|
||||
|
||||
bool canStart() {
|
||||
return !_closed && !_cancelInProgress.value;
|
||||
}
|
||||
|
||||
Future<bool> cancelCurrent(String op) {
|
||||
if (!canStart() || curOP.value != op) {
|
||||
return Future<bool>.value(false);
|
||||
}
|
||||
final authAttempt = ++_authAttempt;
|
||||
final completer = Completer<bool>();
|
||||
_cancelInProgress.value = true;
|
||||
_pendingOperation = _pendingOperation.then((_) async {
|
||||
try {
|
||||
await bind.mainAccountAuthCancel();
|
||||
completer.complete(_isCurrent(authAttempt, op));
|
||||
} catch (error, stackTrace) {
|
||||
completer.completeError(error, stackTrace);
|
||||
} finally {
|
||||
_cancelInProgress.value = false;
|
||||
}
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<void> _cancelBackend() async {
|
||||
try {
|
||||
await bind.mainAccountAuthCancel();
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint('Failed to cancel account authentication $error');
|
||||
debugPrintStack(stackTrace: stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
final hasActiveOidcAuth =
|
||||
curOP.value.isNotEmpty && curOP.value != 'rustdesk';
|
||||
_closed = true;
|
||||
_authAttempt++;
|
||||
curOP.value = '';
|
||||
if (hasActiveOidcAuth) {
|
||||
await _cancelBackend();
|
||||
}
|
||||
await _pendingOperation;
|
||||
if (hasActiveOidcAuth) {
|
||||
await _cancelBackend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WidgetOP extends StatefulWidget {
|
||||
final ConfigOP config;
|
||||
final RxString curOP;
|
||||
final Function(Map<String, dynamic>) cbLogin;
|
||||
final Future<bool> Function(String) startAuth;
|
||||
final Future<bool> Function(String) cancelAuth;
|
||||
final bool Function() canStartAuth;
|
||||
const WidgetOP({
|
||||
Key? key,
|
||||
required this.config,
|
||||
required this.curOP,
|
||||
required this.cbLogin,
|
||||
required this.startAuth,
|
||||
required this.cancelAuth,
|
||||
required this.canStartAuth,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -164,6 +274,8 @@ class WidgetOP extends StatefulWidget {
|
||||
|
||||
class _WidgetOPState extends State<WidgetOP> {
|
||||
Timer? _updateTimer;
|
||||
bool _isAuthStatusQueryInFlight = false;
|
||||
int _authAttempt = 0;
|
||||
String _stateMsg = '';
|
||||
String _failedMsg = '';
|
||||
String _url = '';
|
||||
@@ -174,55 +286,180 @@ class _WidgetOPState extends State<WidgetOP> {
|
||||
_updateTimer?.cancel();
|
||||
}
|
||||
|
||||
_beginQueryState() {
|
||||
_beginQueryState(int authAttempt) {
|
||||
_updateTimer?.cancel();
|
||||
unawaited(_runAuthStatusQuery(() => _updateState(authAttempt)));
|
||||
_updateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
_updateState();
|
||||
unawaited(_runAuthStatusQuery(() => _updateState(authAttempt)));
|
||||
});
|
||||
}
|
||||
|
||||
_updateState() {
|
||||
bind.mainAccountAuthResult().then((result) {
|
||||
if (result.isEmpty) {
|
||||
Future<void> _runAuthStatusQuery(Future<void> Function() query) async {
|
||||
if (_isAuthStatusQueryInFlight) {
|
||||
return;
|
||||
}
|
||||
_isAuthStatusQueryInFlight = true;
|
||||
try {
|
||||
await query();
|
||||
} finally {
|
||||
_isAuthStatusQueryInFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _launchAuthUrl(String url) async {
|
||||
try {
|
||||
final launched = await launchUrl(
|
||||
Uri.parse(url),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
if (!launched) {
|
||||
debugPrint('Failed to open OIDC authentication URL');
|
||||
}
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint(
|
||||
'Failed to open OIDC authentication URL (${error.runtimeType})');
|
||||
debugPrintStack(stackTrace: stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _copyAuthUrl(String url) async {
|
||||
try {
|
||||
await Clipboard.setData(ClipboardData(text: url));
|
||||
showToast(
|
||||
translate('Copied'),
|
||||
);
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint(
|
||||
'Failed to copy OIDC authentication URL (${error.runtimeType})');
|
||||
debugPrintStack(stackTrace: stackTrace);
|
||||
showToast(translate('Failed'));
|
||||
}
|
||||
}
|
||||
|
||||
void _runCurrentAuthUrlAction(
|
||||
int authAttempt,
|
||||
String authUrl,
|
||||
Future<void> Function(String) action,
|
||||
) {
|
||||
if (!mounted ||
|
||||
authAttempt != _authAttempt ||
|
||||
widget.curOP.value != widget.config.op ||
|
||||
authUrl.isEmpty ||
|
||||
_url != authUrl) {
|
||||
return;
|
||||
}
|
||||
unawaited(action(authUrl));
|
||||
}
|
||||
|
||||
void _invalidateAuthAttempt() {
|
||||
_authAttempt++;
|
||||
_url = '';
|
||||
}
|
||||
|
||||
bool _isCurrentAuthAttempt(int authAttempt) {
|
||||
return mounted &&
|
||||
authAttempt == _authAttempt &&
|
||||
widget.curOP.value == widget.config.op;
|
||||
}
|
||||
|
||||
Future<void> _handleAuthFailure(
|
||||
int authAttempt,
|
||||
Object error,
|
||||
String operation,
|
||||
) async {
|
||||
debugPrint('Failed to $operation $error');
|
||||
if (!_isCurrentAuthAttempt(authAttempt)) {
|
||||
return;
|
||||
}
|
||||
_updateTimer?.cancel();
|
||||
setState(() => _failedMsg = 'Failed');
|
||||
try {
|
||||
final canceled = await widget.cancelAuth(widget.config.op);
|
||||
if (!canceled || !_isCurrentAuthAttempt(authAttempt)) {
|
||||
return;
|
||||
}
|
||||
} catch (cancelError, stackTrace) {
|
||||
debugPrint('Failed to cancel account authentication $cancelError');
|
||||
debugPrintStack(stackTrace: stackTrace);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_invalidateAuthAttempt();
|
||||
widget.curOP.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _updateState(int authAttempt) {
|
||||
if (!mounted ||
|
||||
authAttempt != _authAttempt ||
|
||||
widget.curOP.value != widget.config.op) {
|
||||
_updateTimer?.cancel();
|
||||
return Future<void>.value();
|
||||
}
|
||||
return bind.mainAccountAuthResult().then<void>((result) {
|
||||
if (!mounted ||
|
||||
authAttempt != _authAttempt ||
|
||||
widget.curOP.value != widget.config.op ||
|
||||
result.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final resultMap = jsonDecode(result);
|
||||
if (resultMap == null) {
|
||||
return;
|
||||
}
|
||||
final String stateMsg = resultMap['state_msg'];
|
||||
final String backendStateMsg = resultMap['state_msg'];
|
||||
String failedMsg = resultMap['failed_msg'];
|
||||
final String? url = resultMap['url'];
|
||||
final stateMsg = backendStateMsg == _requestingAccountAuth &&
|
||||
(url == null || url.isEmpty)
|
||||
? _waitingAccountAuth
|
||||
: backendStateMsg;
|
||||
final bool urlLaunched = (resultMap['url_launched'] as bool?) ?? false;
|
||||
final authBody = resultMap['auth_body'];
|
||||
if (_stateMsg != stateMsg || _failedMsg != failedMsg) {
|
||||
if (_url.isEmpty && url != null && url.isNotEmpty) {
|
||||
if (!urlLaunched) {
|
||||
launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
|
||||
}
|
||||
_url = url;
|
||||
}
|
||||
if (authBody != null) {
|
||||
_updateTimer?.cancel();
|
||||
widget.curOP.value = '';
|
||||
widget.cbLogin(authBody as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_stateMsg = stateMsg;
|
||||
_failedMsg = failedMsg;
|
||||
if (failedMsg.isNotEmpty) {
|
||||
widget.curOP.value = '';
|
||||
_updateTimer?.cancel();
|
||||
}
|
||||
});
|
||||
if (authBody != null) {
|
||||
_updateTimer?.cancel();
|
||||
_invalidateAuthAttempt();
|
||||
widget.curOP.value = '';
|
||||
widget.cbLogin(authBody as Map<String, dynamic>);
|
||||
return;
|
||||
}
|
||||
});
|
||||
final stateChanged = _stateMsg != stateMsg || _failedMsg != failedMsg;
|
||||
final newUrl = _url.isEmpty && url != null && url.isNotEmpty ? url : null;
|
||||
if (!stateChanged && newUrl == null) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_stateMsg = stateMsg;
|
||||
_failedMsg = failedMsg;
|
||||
if (newUrl != null) {
|
||||
_url = newUrl;
|
||||
}
|
||||
if (failedMsg.isNotEmpty) {
|
||||
_invalidateAuthAttempt();
|
||||
widget.curOP.value = '';
|
||||
_updateTimer?.cancel();
|
||||
}
|
||||
});
|
||||
if (newUrl != null && failedMsg.isEmpty && !urlLaunched) {
|
||||
unawaited(_launchAuthUrl(newUrl));
|
||||
}
|
||||
}).catchError(
|
||||
(e) => _handleAuthFailure(
|
||||
authAttempt,
|
||||
e,
|
||||
'query account authentication',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_resetState() {
|
||||
_stateMsg = '';
|
||||
_failedMsg = '';
|
||||
_url = '';
|
||||
int _resetState() {
|
||||
_updateTimer?.cancel();
|
||||
setState(() {
|
||||
_invalidateAuthAttempt();
|
||||
_stateMsg = _waitingAccountAuth;
|
||||
_failedMsg = '';
|
||||
});
|
||||
return _authAttempt;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -235,11 +472,31 @@ class _WidgetOPState extends State<WidgetOP> {
|
||||
icon: widget.config.icon,
|
||||
primaryColor: str2color(widget.config.op, 0x7f),
|
||||
height: 36,
|
||||
canStartAuth: widget.canStartAuth,
|
||||
onTap: () async {
|
||||
_resetState();
|
||||
widget.curOP.value = widget.config.op;
|
||||
await bind.mainAccountAuth(op: widget.config.op, rememberMe: true);
|
||||
_beginQueryState();
|
||||
if (!widget.canStartAuth()) {
|
||||
return;
|
||||
}
|
||||
final authAttempt = _resetState();
|
||||
try {
|
||||
final started = await widget.startAuth(widget.config.op);
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
await _handleAuthFailure(
|
||||
authAttempt,
|
||||
e,
|
||||
'start account authentication',
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!mounted ||
|
||||
authAttempt != _authAttempt ||
|
||||
widget.curOP.value != widget.config.op) {
|
||||
return;
|
||||
}
|
||||
_beginQueryState(authAttempt);
|
||||
},
|
||||
),
|
||||
Obx(() {
|
||||
@@ -247,6 +504,8 @@ class _WidgetOPState extends State<WidgetOP> {
|
||||
widget.curOP.value != widget.config.op) {
|
||||
_failedMsg = '';
|
||||
}
|
||||
final authAttempt = _authAttempt;
|
||||
final authUrl = _url;
|
||||
return Offstage(
|
||||
offstage:
|
||||
_failedMsg.isEmpty && widget.curOP.value != widget.config.op,
|
||||
@@ -256,11 +515,20 @@ class _WidgetOPState extends State<WidgetOP> {
|
||||
if (_stateMsg.isNotEmpty && _failedMsg.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: SelectableText(
|
||||
translate(_stateMsg),
|
||||
style: DefaultTextStyle.of(context)
|
||||
.style
|
||||
.copyWith(fontSize: 12),
|
||||
child: OidcAuthStatus(
|
||||
message: translate(_stateMsg),
|
||||
browserFallbackPrompt: translate(
|
||||
"Browser didn't open? Use the url below to sign in.",
|
||||
),
|
||||
authUrl: authUrl,
|
||||
copyLabel: translate('Copy to clipboard'),
|
||||
onCopy: authUrl.isEmpty
|
||||
? null
|
||||
: () => _runCurrentAuthUrlAction(
|
||||
authAttempt,
|
||||
authUrl,
|
||||
_copyAuthUrl,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_failedMsg.isNotEmpty)
|
||||
@@ -304,34 +572,6 @@ class _WidgetOPState extends State<WidgetOP> {
|
||||
),
|
||||
);
|
||||
}),
|
||||
Obx(
|
||||
() => Offstage(
|
||||
offstage: widget.curOP.value != widget.config.op,
|
||||
child: const SizedBox(
|
||||
height: 5.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => Offstage(
|
||||
offstage: widget.curOP.value != widget.config.op,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: 20),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
widget.curOP.value = '';
|
||||
_updateTimer?.cancel();
|
||||
_resetState();
|
||||
bind.mainAccountAuthCancel();
|
||||
},
|
||||
child: Text(
|
||||
translate('Cancel'),
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -341,12 +581,18 @@ class LoginWidgetOP extends StatelessWidget {
|
||||
final List<ConfigOP> ops;
|
||||
final RxString curOP;
|
||||
final Function(Map<String, dynamic>) cbLogin;
|
||||
final Future<bool> Function(String) startAuth;
|
||||
final Future<bool> Function(String) cancelAuth;
|
||||
final bool Function() canStartAuth;
|
||||
|
||||
LoginWidgetOP({
|
||||
Key? key,
|
||||
required this.ops,
|
||||
required this.curOP,
|
||||
required this.cbLogin,
|
||||
required this.startAuth,
|
||||
required this.cancelAuth,
|
||||
required this.canStartAuth,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -357,6 +603,9 @@ class LoginWidgetOP extends StatelessWidget {
|
||||
config: op,
|
||||
curOP: curOP,
|
||||
cbLogin: cbLogin,
|
||||
startAuth: startAuth,
|
||||
cancelAuth: cancelAuth,
|
||||
canStartAuth: canStartAuth,
|
||||
),
|
||||
const Divider(
|
||||
indent: 5,
|
||||
@@ -434,12 +683,11 @@ class LoginWidgetUserPass extends StatelessWidget {
|
||||
translate('Login'),
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
onPressed:
|
||||
curOP.value.isEmpty || curOP.value == 'rustdesk'
|
||||
? () {
|
||||
onLogin();
|
||||
}
|
||||
: null,
|
||||
onPressed: curOP.value.isEmpty && !isInProgress
|
||||
? () {
|
||||
onLogin();
|
||||
}
|
||||
: null,
|
||||
)),
|
||||
),
|
||||
])),
|
||||
@@ -450,8 +698,28 @@ class LoginWidgetUserPass extends StatelessWidget {
|
||||
|
||||
const kAuthReqTypeOidc = 'oidc/';
|
||||
|
||||
Future<bool?>? _activeLoginDialog;
|
||||
|
||||
// call this directly
|
||||
Future<bool?> loginDialog() async {
|
||||
Future<bool?> loginDialog() {
|
||||
final activeDialog = _activeLoginDialog;
|
||||
if (activeDialog != null) {
|
||||
return activeDialog;
|
||||
}
|
||||
final dialog = _openLoginDialogOnce();
|
||||
_activeLoginDialog = dialog;
|
||||
return dialog;
|
||||
}
|
||||
|
||||
Future<bool?> _openLoginDialogOnce() async {
|
||||
try {
|
||||
return await _openLoginDialog();
|
||||
} finally {
|
||||
_activeLoginDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool?> _openLoginDialog() async {
|
||||
var username =
|
||||
TextEditingController(text: UserModel.getLocalUserInfo()?['name'] ?? '');
|
||||
var password = TextEditingController();
|
||||
@@ -461,12 +729,13 @@ Future<bool?> loginDialog() async {
|
||||
String? usernameMsg;
|
||||
String? passwordMsg;
|
||||
var isInProgress = false;
|
||||
final RxString curOP = ''.obs;
|
||||
final oidcAuth = _OidcAuthController();
|
||||
final curOP = oidcAuth.curOP;
|
||||
// Track hover state for the close icon
|
||||
bool isCloseHovered = false;
|
||||
|
||||
final loginOptions = [].obs;
|
||||
final loginOptionsError = Rxn<String>();
|
||||
final loginOptionsError = Rxn<Object>();
|
||||
final loginOptionsInProgress = false.obs;
|
||||
fetchLoginOptions() async {
|
||||
loginOptionsInProgress.value = true;
|
||||
@@ -475,7 +744,7 @@ Future<bool?> loginDialog() async {
|
||||
loginOptionsError.value = null;
|
||||
} catch (e) {
|
||||
debugPrint("queryOidcLoginOptions failed: $e");
|
||||
loginOptionsError.value = e.toString();
|
||||
loginOptionsError.value = e;
|
||||
} finally {
|
||||
loginOptionsInProgress.value = false;
|
||||
}
|
||||
@@ -555,6 +824,9 @@ Future<bool?> loginDialog() async {
|
||||
}
|
||||
|
||||
onLogin() async {
|
||||
if (curOP.value.isNotEmpty || isInProgress) {
|
||||
return;
|
||||
}
|
||||
// validate
|
||||
if (username.text.isEmpty) {
|
||||
setState(() => usernameMsg = translate('Username missed'));
|
||||
@@ -593,7 +865,7 @@ Future<bool?> loginDialog() async {
|
||||
const SizedBox(height: 8.0),
|
||||
// NOT use Offstage to wrap LinearProgressIndicator
|
||||
if (inProgress) const LinearProgressIndicator(),
|
||||
if (!inProgress)
|
||||
if (!inProgress && error is! RequestException)
|
||||
Text(
|
||||
translate('network_error_tip'),
|
||||
style: const TextStyle(fontSize: 12),
|
||||
@@ -608,7 +880,7 @@ Future<bool?> loginDialog() async {
|
||||
),
|
||||
if (!inProgress)
|
||||
SelectableText(
|
||||
error,
|
||||
error.toString(),
|
||||
style: const TextStyle(fontSize: 11, color: Colors.red),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -635,6 +907,9 @@ Future<bool?> loginDialog() async {
|
||||
.map((e) => ConfigOP(op: e['name'], icon: e['icon']))
|
||||
.toList(),
|
||||
curOP: curOP,
|
||||
startAuth: oidcAuth.start,
|
||||
cancelAuth: oidcAuth.cancelCurrent,
|
||||
canStartAuth: oidcAuth.canStart,
|
||||
cbLogin: (Map<String, dynamic> authBody) async {
|
||||
LoginResponse? resp;
|
||||
try {
|
||||
@@ -716,7 +991,7 @@ Future<bool?> loginDialog() async {
|
||||
onCancel: onDialogCancel,
|
||||
onSubmit: onLogin,
|
||||
);
|
||||
});
|
||||
}).whenComplete(oidcAuth.close);
|
||||
|
||||
if (res != null) {
|
||||
await UserModel.updateOtherModels();
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const _statusFontSize = 12.0;
|
||||
const _statusSpacing = 4.0;
|
||||
const _messageActionSpacing = 8.0;
|
||||
const _desktopActionSize = 28.0;
|
||||
const _touchPlatforms = <TargetPlatform>{
|
||||
TargetPlatform.android,
|
||||
TargetPlatform.iOS,
|
||||
TargetPlatform.fuchsia,
|
||||
};
|
||||
|
||||
class OidcAuthStatus extends StatelessWidget {
|
||||
final String message;
|
||||
final String browserFallbackPrompt;
|
||||
final String authUrl;
|
||||
final String copyLabel;
|
||||
final VoidCallback? onCopy;
|
||||
|
||||
const OidcAuthStatus({
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.browserFallbackPrompt,
|
||||
required this.authUrl,
|
||||
required this.copyLabel,
|
||||
this.onCopy,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final messageStyle =
|
||||
DefaultTextStyle.of(context).style.copyWith(fontSize: _statusFontSize);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SelectableText(message, style: messageStyle),
|
||||
if (authUrl.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: _messageActionSpacing),
|
||||
child: _OidcAuthFallback(
|
||||
browserFallbackPrompt: browserFallbackPrompt,
|
||||
authUrl: authUrl,
|
||||
copyLabel: copyLabel,
|
||||
onCopy: onCopy,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OidcAuthFallback extends StatefulWidget {
|
||||
final String browserFallbackPrompt;
|
||||
final String authUrl;
|
||||
final String copyLabel;
|
||||
final VoidCallback? onCopy;
|
||||
|
||||
const _OidcAuthFallback({
|
||||
required this.browserFallbackPrompt,
|
||||
required this.authUrl,
|
||||
required this.copyLabel,
|
||||
required this.onCopy,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_OidcAuthFallback> createState() => _OidcAuthFallbackState();
|
||||
}
|
||||
|
||||
class _OidcAuthFallbackState extends State<_OidcAuthFallback> {
|
||||
bool _expanded = false;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _OidcAuthFallback oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.authUrl != widget.authUrl) {
|
||||
_expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final helperStyle = DefaultTextStyle.of(context).style.copyWith(
|
||||
fontSize: _statusFontSize,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
);
|
||||
final linkColor = theme.brightness == Brightness.dark
|
||||
? Colors.blue.shade300
|
||||
: Colors.blue.shade800;
|
||||
final isTouchPlatform = _touchPlatforms.contains(theme.platform);
|
||||
final actionSize =
|
||||
isTouchPlatform ? kMinInteractiveDimension : _desktopActionSize;
|
||||
final urlStyle =
|
||||
DefaultTextStyle.of(context).style.copyWith(fontSize: _statusFontSize);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
widget.browserFallbackPrompt,
|
||||
style: helperStyle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: _statusSpacing),
|
||||
child: _buildUrl(urlStyle, linkColor, actionSize),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _copyAndExpand() {
|
||||
setState(() => _expanded = true);
|
||||
widget.onCopy?.call();
|
||||
}
|
||||
|
||||
Widget _buildUrl(TextStyle urlStyle, Color linkColor, double actionSize) {
|
||||
final collapsedUrl = SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: linkColor,
|
||||
minimumSize: Size(0, actionSize),
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.standard,
|
||||
),
|
||||
onPressed: _copyAndExpand,
|
||||
child: Text(
|
||||
widget.authUrl,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
style: urlStyle.copyWith(
|
||||
color: linkColor,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final collapsedChild = widget.onCopy == null
|
||||
? collapsedUrl
|
||||
: Tooltip(message: widget.copyLabel, child: collapsedUrl);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
constraints: BoxConstraints(minHeight: actionSize),
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(horizontal: _messageActionSpacing),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Theme.of(context).dividerColor),
|
||||
borderRadius: BorderRadius.circular(_statusSpacing),
|
||||
),
|
||||
child: _expanded
|
||||
? SelectableText(widget.authUrl, style: urlStyle)
|
||||
: collapsedChild,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -234,8 +234,9 @@ class UserModel {
|
||||
return loginResponse;
|
||||
}
|
||||
|
||||
/// Throws on network failure so callers can surface the error and offer a
|
||||
/// retry; returns an empty list when the server has no third-party login.
|
||||
/// Throws on network failures, non-success responses, and invalid response
|
||||
/// data. Returns an empty list when no API server is configured or a
|
||||
/// successful response contains no third-party login options.
|
||||
static Future<List<dynamic>> queryOidcLoginOptions() async {
|
||||
final url = await bind.mainGetApiServer();
|
||||
if (url.trim().isEmpty) return [];
|
||||
|
||||
+82
-49
@@ -113,7 +113,7 @@ pub struct OidcSession {
|
||||
failed_msg: String,
|
||||
code_url: Option<OidcAuthUrl>,
|
||||
auth_body: Option<AuthBody>,
|
||||
keep_querying: bool,
|
||||
auth_attempt: u64,
|
||||
running: bool,
|
||||
query_timeout: Duration,
|
||||
}
|
||||
@@ -140,7 +140,7 @@ impl OidcSession {
|
||||
failed_msg: "".to_owned(),
|
||||
code_url: None,
|
||||
auth_body: None,
|
||||
keep_querying: false,
|
||||
auth_attempt: 0,
|
||||
running: false,
|
||||
query_timeout: Duration::from_secs(QUERY_TIMEOUT_SECS),
|
||||
}
|
||||
@@ -192,12 +192,8 @@ impl OidcSession {
|
||||
body: String,
|
||||
}
|
||||
|
||||
let resp = crate::http_request_sync(
|
||||
url.to_string(),
|
||||
"GET".to_owned(),
|
||||
None,
|
||||
"{}".to_owned(),
|
||||
)?;
|
||||
let resp =
|
||||
crate::http_request_sync(url.to_string(), "GET".to_owned(), None, "{}".to_owned())?;
|
||||
let resp = serde_json::from_str::<HttpResponseBody>(&resp)?;
|
||||
HbbHttpResponse::parse(&resp.body)
|
||||
}
|
||||
@@ -205,7 +201,6 @@ impl OidcSession {
|
||||
fn reset(&mut self) {
|
||||
self.state_msg = REQUESTING_ACCOUNT_AUTH;
|
||||
self.failed_msg = "".to_owned();
|
||||
self.keep_querying = true;
|
||||
self.running = false;
|
||||
self.code_url = None;
|
||||
self.auth_body = None;
|
||||
@@ -220,49 +215,92 @@ impl OidcSession {
|
||||
self.running = false;
|
||||
}
|
||||
|
||||
fn start_auth_attempt(&mut self) -> u64 {
|
||||
self.auth_attempt = self.auth_attempt.wrapping_add(1);
|
||||
self.auth_attempt
|
||||
}
|
||||
|
||||
fn cancel_auth_attempt(&mut self) {
|
||||
self.auth_attempt = self.auth_attempt.wrapping_add(1);
|
||||
}
|
||||
|
||||
fn is_current_auth_attempt(&self, auth_attempt: u64) -> bool {
|
||||
self.auth_attempt == auth_attempt
|
||||
}
|
||||
|
||||
fn auth_attempt_is_current(auth_attempt: u64) -> bool {
|
||||
OIDC_SESSION
|
||||
.read()
|
||||
.unwrap()
|
||||
.is_current_auth_attempt(auth_attempt)
|
||||
}
|
||||
|
||||
fn set_state_if_current(auth_attempt: u64, state_msg: &'static str, failed_msg: String) {
|
||||
let mut session = OIDC_SESSION.write().unwrap();
|
||||
if session.is_current_auth_attempt(auth_attempt) {
|
||||
session.set_state(state_msg, failed_msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn sleep(secs: f32) {
|
||||
std::thread::sleep(std::time::Duration::from_secs_f32(secs));
|
||||
}
|
||||
|
||||
fn auth_task(api_server: String, op: String, id: String, uuid: String, remember_me: bool) {
|
||||
fn auth_task(
|
||||
api_server: String,
|
||||
op: String,
|
||||
id: String,
|
||||
uuid: String,
|
||||
remember_me: bool,
|
||||
auth_attempt: u64,
|
||||
) {
|
||||
let auth_request_res = Self::auth(&api_server, &op, &id, &uuid);
|
||||
log::info!("Request oidc auth result: {:?}", &auth_request_res);
|
||||
if !Self::auth_attempt_is_current(auth_attempt) {
|
||||
return;
|
||||
}
|
||||
let code_url = match auth_request_res {
|
||||
Ok(HbbHttpResponse::<_>::Data(code_url)) => code_url,
|
||||
Ok(HbbHttpResponse::<_>::Error(err)) => {
|
||||
OIDC_SESSION
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_state(REQUESTING_ACCOUNT_AUTH, err);
|
||||
Self::set_state_if_current(auth_attempt, REQUESTING_ACCOUNT_AUTH, err);
|
||||
return;
|
||||
}
|
||||
Ok(_) => {
|
||||
OIDC_SESSION
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_state(REQUESTING_ACCOUNT_AUTH, "Invalid auth response".to_owned());
|
||||
Self::set_state_if_current(
|
||||
auth_attempt,
|
||||
REQUESTING_ACCOUNT_AUTH,
|
||||
"Invalid auth response".to_owned(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
OIDC_SESSION
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_state(REQUESTING_ACCOUNT_AUTH, err.to_string());
|
||||
Self::set_state_if_current(auth_attempt, REQUESTING_ACCOUNT_AUTH, err.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
OIDC_SESSION
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_state(WAITING_ACCOUNT_AUTH, "".to_owned());
|
||||
OIDC_SESSION.write().unwrap().code_url = Some(code_url.clone());
|
||||
{
|
||||
let mut session = OIDC_SESSION.write().unwrap();
|
||||
if !session.is_current_auth_attempt(auth_attempt) {
|
||||
return;
|
||||
}
|
||||
session.set_state(WAITING_ACCOUNT_AUTH, "".to_owned());
|
||||
session.code_url = Some(code_url.clone());
|
||||
}
|
||||
|
||||
let begin = Instant::now();
|
||||
let query_timeout = OIDC_SESSION.read().unwrap().query_timeout;
|
||||
while OIDC_SESSION.read().unwrap().keep_querying && begin.elapsed() < query_timeout {
|
||||
match Self::query(&api_server, &code_url.code, &id, &uuid) {
|
||||
while Self::auth_attempt_is_current(auth_attempt) && begin.elapsed() < query_timeout {
|
||||
let query_result = Self::query(&api_server, &code_url.code, &id, &uuid);
|
||||
if !Self::auth_attempt_is_current(auth_attempt) {
|
||||
return;
|
||||
}
|
||||
match query_result {
|
||||
Ok(HbbHttpResponse::<_>::Data(auth_body)) => {
|
||||
let mut session = OIDC_SESSION.write().unwrap();
|
||||
if !session.is_current_auth_attempt(auth_attempt) {
|
||||
return;
|
||||
}
|
||||
if auth_body.r#type == "access_token" {
|
||||
if remember_me {
|
||||
LocalConfig::set_option(
|
||||
@@ -281,21 +319,15 @@ impl OidcSession {
|
||||
);
|
||||
}
|
||||
}
|
||||
OIDC_SESSION
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_state(LOGIN_ACCOUNT_AUTH, "".to_owned());
|
||||
OIDC_SESSION.write().unwrap().auth_body = Some(auth_body);
|
||||
session.set_state(LOGIN_ACCOUNT_AUTH, "".to_owned());
|
||||
session.auth_body = Some(auth_body);
|
||||
return;
|
||||
}
|
||||
Ok(HbbHttpResponse::<_>::Error(err)) => {
|
||||
if err.contains("No authed oidc is found") {
|
||||
// ignore, keep querying
|
||||
} else {
|
||||
OIDC_SESSION
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_state(WAITING_ACCOUNT_AUTH, err);
|
||||
Self::set_state_if_current(auth_attempt, WAITING_ACCOUNT_AUTH, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -310,14 +342,9 @@ impl OidcSession {
|
||||
Self::sleep(QUERY_INTERVAL_SECS);
|
||||
}
|
||||
|
||||
if begin.elapsed() >= query_timeout {
|
||||
OIDC_SESSION
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_state(WAITING_ACCOUNT_AUTH, "timeout".to_owned());
|
||||
if begin.elapsed() >= query_timeout && Self::auth_attempt_is_current(auth_attempt) {
|
||||
Self::set_state_if_current(auth_attempt, WAITING_ACCOUNT_AUTH, "timeout".to_owned());
|
||||
}
|
||||
|
||||
// no need to handle "keep_querying == false"
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state_msg: &'static str, failed_msg: String) {
|
||||
@@ -339,11 +366,17 @@ impl OidcSession {
|
||||
uuid: String,
|
||||
remember_me: bool,
|
||||
) {
|
||||
Self::auth_cancel();
|
||||
let auth_attempt = OIDC_SESSION.write().unwrap().start_auth_attempt();
|
||||
Self::wait_stop_querying();
|
||||
OIDC_SESSION.write().unwrap().before_task();
|
||||
{
|
||||
let mut session = OIDC_SESSION.write().unwrap();
|
||||
if !session.is_current_auth_attempt(auth_attempt) {
|
||||
return;
|
||||
}
|
||||
session.before_task();
|
||||
}
|
||||
std::thread::spawn(move || {
|
||||
Self::auth_task(api_server, op, id, uuid, remember_me);
|
||||
Self::auth_task(api_server, op, id, uuid, remember_me, auth_attempt);
|
||||
OIDC_SESSION.write().unwrap().after_task();
|
||||
});
|
||||
}
|
||||
@@ -358,7 +391,7 @@ impl OidcSession {
|
||||
}
|
||||
|
||||
pub fn auth_cancel() {
|
||||
OIDC_SESSION.write().unwrap().keep_querying = false;
|
||||
OIDC_SESSION.write().unwrap().cancel_auth_attempt();
|
||||
}
|
||||
|
||||
pub fn get_result() -> AuthResult {
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "اتصال الوسيط"),
|
||||
("Secure Connection", "اتصال آمن"),
|
||||
("Insecure Connection", "اتصال غير آمن"),
|
||||
("Continue", ""),
|
||||
("Scale original", "المقياس الأصلي"),
|
||||
("Scale adaptive", "مقياس التكيف"),
|
||||
("General", "عام"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "تم حظر عنوان IP الخاص بك من قبل الطرف الآخر"),
|
||||
("id_whitelist_caveat_tip", "يتم الإبلاغ عن المعرف من قبل العميل المتصل. القائمة البيضاء تقلل من التعرض ولا تغني عن كلمة المرور أو 2FA"),
|
||||
("whitelist_cidr_tip", "يتم دعم صيغة CIDR، مثال: 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Рэтрансляванае падключэнне"),
|
||||
("Secure Connection", "Бяспечнае падключэнне"),
|
||||
("Insecure Connection", "Нябяспечнае падключэнне"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Арыгінальны маштаб"),
|
||||
("Scale adaptive", "Адаптыўны маштаб"),
|
||||
("General", "Агульныя"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Ваш IP-адрас заблакаваны аддаленай прыладай"),
|
||||
("id_whitelist_caveat_tip", "ID паведамляецца кліентам, які падключаецца. Белы спіс памяншае паверхню атакі і не замяняе пароль або 2FA"),
|
||||
("whitelist_cidr_tip", "Падтрымліваецца натацыя CIDR, напрыклад: 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Релейна връзка"),
|
||||
("Secure Connection", "Сигурна връзка"),
|
||||
("Insecure Connection", "Несигурна връзка"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Оригинален мащаб"),
|
||||
("Scale adaptive", "Приспособимо мащабиране"),
|
||||
("General", "Основен"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Вашият IP адрес е блокиран от отсрещната страна"),
|
||||
("id_whitelist_caveat_tip", "ID се съобщава от свързващия се клиент. Белият списък намалява изложеността и не замества паролата или 2FA"),
|
||||
("whitelist_cidr_tip", "Поддържа се CIDR нотация, например: 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Connexió amb repetidor"),
|
||||
("Secure Connection", "Connexió segura"),
|
||||
("Insecure Connection", "Connexió no segura"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Escala original"),
|
||||
("Scale adaptive", "Escala adaptativa"),
|
||||
("General", "General"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "La vostra IP està bloquejada per l'altre extrem"),
|
||||
("id_whitelist_caveat_tip", "L'ID és informat pel client que es connecta. Aquesta llista blanca redueix l'exposició i no substitueix la contrasenya ni la 2FA"),
|
||||
("whitelist_cidr_tip", "S'admet la notació CIDR, per exemple 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "中继连接"),
|
||||
("Secure Connection", "安全连接"),
|
||||
("Insecure Connection", "非安全连接"),
|
||||
("Continue", "继续"),
|
||||
("Scale original", "原始尺寸"),
|
||||
("Scale adaptive", "适应窗口"),
|
||||
("General", "常规"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "你的 IP 已被对方阻止"),
|
||||
("id_whitelist_caveat_tip", "ID 由对端客户端上报,白名单用于减少暴露面,不能替代密码或 2FA"),
|
||||
("whitelist_cidr_tip", "支持 CIDR 写法,例如 192.168.1.0/24"),
|
||||
("Continue", "继续"),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Připojení předávací server"),
|
||||
("Secure Connection", "Zabezpečené připojení"),
|
||||
("Insecure Connection", "Nezabezpečené připojení"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Originální měřítko"),
|
||||
("Scale adaptive", "Adaptivní měřítko"),
|
||||
("General", "Obecné"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Vaše IP adresa je protistranou blokována"),
|
||||
("id_whitelist_caveat_tip", "ID je hlášeno připojujícím se klientem. Tento seznam snižuje vystavení a nenahrazuje heslo ani 2FA"),
|
||||
("whitelist_cidr_tip", "Je podporován zápis CIDR, například 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Viderestillingsforbindelse"),
|
||||
("Secure Connection", "Sikker forbindelse"),
|
||||
("Insecure Connection", "Usikker forbindelse"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Original skalering"),
|
||||
("Scale adaptive", "Adaptiv skalering"),
|
||||
("General", "Generelt"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Din IP-adresse er blokeret af modparten"),
|
||||
("id_whitelist_caveat_tip", "ID'et rapporteres af den klient, der opretter forbindelse. Whitelisten reducerer eksponeringen og erstatter ikke adgangskode eller 2FA"),
|
||||
("whitelist_cidr_tip", "CIDR-notation understøttes, f.eks. 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Relay-Verbindung"),
|
||||
("Secure Connection", "Sichere Verbindung"),
|
||||
("Insecure Connection", "Unsichere Verbindung"),
|
||||
("Continue", "Weiter"),
|
||||
("Scale original", "Keine Skalierung"),
|
||||
("Scale adaptive", "Anpassbare Skalierung"),
|
||||
("General", "Allgemein"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Ihre IP-Adresse wird von der Gegenstelle blockiert"),
|
||||
("id_whitelist_caveat_tip", "Die ID wird vom verbindenden Client gemeldet. Die Whitelist verringert die Angriffsfläche und ersetzt weder Passwort noch 2FA."),
|
||||
("whitelist_cidr_tip", "Die CIDR-Notation wird unterstützt, z. B. 192.168.1.0/24"),
|
||||
("Continue", "Weiter"),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Αναμεταδιδόμενη σύνδεση"),
|
||||
("Secure Connection", "Ασφαλής σύνδεση"),
|
||||
("Insecure Connection", "Μη ασφαλής σύνδεση"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Κλιμάκωση πρωτότυπου"),
|
||||
("Scale adaptive", "Προσαρμοσμένη κλίμακα"),
|
||||
("General", "Γενικά"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Η διεύθυνση IP σας έχει αποκλειστεί από τον απομακρυσμένο υπολογιστή"),
|
||||
("id_whitelist_caveat_tip", "Το ID αναφέρεται από τον πελάτη που συνδέεται. Η λίστα επιτρεπόμενων μειώνει την έκθεση και δεν αντικαθιστά τον κωδικό πρόσβασης ή το 2FA"),
|
||||
("whitelist_cidr_tip", "Υποστηρίζεται η σημειογραφία CIDR, π.χ. 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Relajsa Konekto"),
|
||||
("Secure Connection", "Sekura Konekto"),
|
||||
("Insecure Connection", "Nesekura Konekto"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Skalo originalo"),
|
||||
("Scale adaptive", "Skalo adapta"),
|
||||
("General", "Ĝenerala"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Via IP estas blokita de la alia flanko"),
|
||||
("id_whitelist_caveat_tip", "La ID estas raportata de la konektiĝanta kliento. La blanka listo malpliigas la eksponiĝon kaj ne anstataŭas la pasvorton aŭ 2FA"),
|
||||
("whitelist_cidr_tip", "La notacio CIDR estas subtenata, ekzemple 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Conexión Relay"),
|
||||
("Secure Connection", "Conexión segura"),
|
||||
("Insecure Connection", "Conexión insegura"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Escala original"),
|
||||
("Scale adaptive", "Escala adaptativa"),
|
||||
("General", "General"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Tu IP está bloqueada por el dispositivo remoto"),
|
||||
("id_whitelist_caveat_tip", "El ID lo comunica el cliente que se conecta. Esta lista blanca reduce la exposición y no sustituye a la contraseña ni al 2FA"),
|
||||
("whitelist_cidr_tip", "Se admite la notación CIDR, por ejemplo 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Releeühendus"),
|
||||
("Secure Connection", "Turvaline ühendus"),
|
||||
("Insecure Connection", "Ebaturvaline ühendus"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Originaalskaala"),
|
||||
("Scale adaptive", "Kohanduv skaala"),
|
||||
("General", "Üldine"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Teine pool on sinu IP-aadressi blokeerinud"),
|
||||
("id_whitelist_caveat_tip", "ID edastab ühenduv klient. Lubamisloend vähendab eksponeeritust ega asenda parooli või 2FA-d"),
|
||||
("whitelist_cidr_tip", "Toetatud on CIDR-tähistus, näiteks 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Konexio igorria"),
|
||||
("Secure Connection", "Konexio segurua"),
|
||||
("Insecure Connection", "Konexio ez-segurua"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Jatorrizko eskala"),
|
||||
("Scale adaptive", "Eskala moldagarria"),
|
||||
("General", "Orokorra"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Beste aldeak zure IP helbidea blokeatu du"),
|
||||
("id_whitelist_caveat_tip", "IDa konektatzen den bezeroak jakinarazten du. Zerrenda honek esposizioa murrizten du eta ez du pasahitza edo 2FA ordezkatzen"),
|
||||
("whitelist_cidr_tip", "CIDR notazioa onartzen da, adibidez 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Relay ارتباط"),
|
||||
("Secure Connection", "ارتباط امن"),
|
||||
("Insecure Connection", "ارتباط غیر امن"),
|
||||
("Continue", ""),
|
||||
("Scale original", "مقیاس اصلی"),
|
||||
("Scale adaptive", "مقیاس تطبیقی"),
|
||||
("General", "عمومی"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "نشانی IP شما توسط طرف مقابل مسدود شده است"),
|
||||
("id_whitelist_caveat_tip", "شناسه توسط کلاینت متصل شونده گزارش می شود. لیست مجاز سطح در معرض بودن را کاهش می دهد و جایگزین رمز عبور یا 2FA نیست"),
|
||||
("whitelist_cidr_tip", "نماد CIDR پشتیبانی می شود، برای مثال 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Välitetty yhteys"),
|
||||
("Secure Connection", "Suojattu yhteys"),
|
||||
("Insecure Connection", "Suojaamaton yhteys"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Skaalaa alkuperäinen"),
|
||||
("Scale adaptive", "Mukautuva skaalaus"),
|
||||
("General", "Yleiset"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Vastapuoli on estänyt IP-osoitteesi"),
|
||||
("id_whitelist_caveat_tip", "ID on yhdistävän asiakkaan ilmoittama. Sallintalista pienentää altistusta eikä korvaa salasanaa tai 2FA:ta"),
|
||||
("whitelist_cidr_tip", "CIDR-merkintä on tuettu, esimerkiksi 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Connexion via relais"),
|
||||
("Secure Connection", "Connexion sécurisée"),
|
||||
("Insecure Connection", "Connexion non sécurisée"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Échelle originale"),
|
||||
("Scale adaptive", "Échelle adaptative"),
|
||||
("General", "Général"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Votre adresse IP est bloquée par l’appareil distant"),
|
||||
("id_whitelist_caveat_tip", "L’ID est déclaré par le client qui se connecte. Cette liste blanche réduit l’exposition et ne remplace ni le mot de passe ni la 2FA"),
|
||||
("whitelist_cidr_tip", "La notation CIDR est prise en charge, par exemple 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "რეტრანსლირებული კავშირი"),
|
||||
("Secure Connection", "უსაფრთხო კავშირი"),
|
||||
("Insecure Connection", "არაუსაფრთხო კავშირი"),
|
||||
("Continue", ""),
|
||||
("Scale original", "ორიგინალური მასშტაბი"),
|
||||
("Scale adaptive", "ადაპტირებადი მასშტაბი"),
|
||||
("General", "ზოგადი"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "თქვენი IP მისამართი დაბლოკილია მეორე მხარის მიერ"),
|
||||
("id_whitelist_caveat_tip", "ID-ს აცხადებს დამაკავშირებელი კლიენტი. თეთრი სია ამცირებს ექსპოზიციას და ვერ ჩაანაცვლებს პაროლს ან 2FA-ს"),
|
||||
("whitelist_cidr_tip", "მხარდაჭერილია CIDR ჩანაწერი, მაგალითად 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "રિલે કનેક્શન"),
|
||||
("Secure Connection", "સુરક્ષિત કનેક્શન"),
|
||||
("Insecure Connection", "અસુરક્ષિત કનેક્શન"),
|
||||
("Continue", ""),
|
||||
("Scale original", "મૂળ સ્કેલ"),
|
||||
("Scale adaptive", "એડેપ્ટિવ સ્કેલ"),
|
||||
("General", "સામાન્ય"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "તમારું IP સામેના પક્ષ દ્વારા બ્લોક કરવામાં આવ્યું છે"),
|
||||
("id_whitelist_caveat_tip", "ID કનેક્ટ થતા ક્લાયન્ટ દ્વારા જણાવવામાં આવે છે. વ્હાઇટલિસ્ટ એક્સપોઝર ઘટાડે છે અને પાસવર્ડ કે 2FA નો વિકલ્પ નથી"),
|
||||
("whitelist_cidr_tip", "CIDR નોટેશન સપોર્ટેડ છે, ઉदાહરણ તરીકે 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "חיבור באמצעות ממסר"),
|
||||
("Secure Connection", "חיבור מאובטח"),
|
||||
("Insecure Connection", "חיבור לא מאובטח"),
|
||||
("Continue", ""),
|
||||
("Scale original", "קנה מידה מקורי"),
|
||||
("Scale adaptive", "קנה מידה מותאם"),
|
||||
("General", "כללי"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "כתובת ה-IP שלך נחסמה על ידי הצד המרוחק"),
|
||||
("id_whitelist_caveat_tip", "המזהה מדווח על ידי הלקוח המתחבר. הרשימה הלבנה מצמצמת חשיפה ואינה מחליפה סיסמה או 2FA"),
|
||||
("whitelist_cidr_tip", "יש תמיכה בסימון CIDR, לדוגמה 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "रिले कनेक्शन"),
|
||||
("Secure Connection", "सुरक्षित कनेक्शन"),
|
||||
("Insecure Connection", "असुरक्षित कनेक्शन"),
|
||||
("Continue", ""),
|
||||
("Scale original", "मूल पैमाना"),
|
||||
("Scale adaptive", "अनुकूली पैमाना"),
|
||||
("General", "सामान्य"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "आपका IP दूसरे पक्ष द्वारा अवरुद्ध कर दिया गया है"),
|
||||
("id_whitelist_caveat_tip", "ID कनेक्ट करने वाले क्लाइंट द्वारा बताई जाती है। श्वेतसूची जोखिम कम करती है और पासवर्ड या 2FA का विकल्प नहीं है"),
|
||||
("whitelist_cidr_tip", "CIDR नोटेशन समर्थित है, उदाहरण के लिए 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Posredna veza"),
|
||||
("Secure Connection", "Sigurna veza"),
|
||||
("Insecure Connection", "Nesigurna veza"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Skaliraj izvornik"),
|
||||
("Scale adaptive", "Prilagođeno skaliranje"),
|
||||
("General", "Općenito"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Vašu IP adresu je blokiralo udaljeno računalo"),
|
||||
("id_whitelist_caveat_tip", "ID prijavljuje klijent koji se povezuje. Ova lista smanjuje izloženost i ne zamjenjuje lozinku ni 2FA"),
|
||||
("whitelist_cidr_tip", "Podržan je CIDR zapis, primjerice 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Kapcsolódás továbbító-kiszolgálón keresztül"),
|
||||
("Secure Connection", "Biztonságos kapcsolat"),
|
||||
("Insecure Connection", "Nem biztonságos kapcsolat"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Eredeti méretarány"),
|
||||
("Scale adaptive", "Adaptív méretarány"),
|
||||
("General", "Általános"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Az IP-címét a távoli fél letiltotta"),
|
||||
("id_whitelist_caveat_tip", "Az azonosítót a csatlakozó kliens jelenti. Az engedélyezési lista csökkenti a kitettséget, és nem helyettesíti a jelszót vagy a 2FA-t"),
|
||||
("whitelist_cidr_tip", "A CIDR jelölés támogatott, például 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Koneksi Relay"),
|
||||
("Secure Connection", "Koneksi aman"),
|
||||
("Insecure Connection", "Koneksi Tidak Aman"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Skala asli"),
|
||||
("Scale adaptive", "Skala adaptif"),
|
||||
("General", "Umum"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "IP Anda diblokir oleh perangkat remote"),
|
||||
("id_whitelist_caveat_tip", "ID dilaporkan oleh klien yang terhubung. Daftar ini mengurangi paparan dan bukan pengganti kata sandi atau 2FA"),
|
||||
("whitelist_cidr_tip", "Notasi CIDR didukung, misalnya 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Connessione relay"),
|
||||
("Secure Connection", "Connessione sicura"),
|
||||
("Insecure Connection", "Connessione non sicura"),
|
||||
("Continue", "Continua"),
|
||||
("Scale original", "Scala originale"),
|
||||
("Scale adaptive", "Scala adattiva"),
|
||||
("General", "Generale"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Il tuo IP è bloccato dal dispositivo remoto"),
|
||||
("id_whitelist_caveat_tip", "L'ID è dichiarato dal client che si connette. Questo elenco riduce l'esposizione e non sostituisce la password o la 2FA"),
|
||||
("whitelist_cidr_tip", "È supportata la notazione CIDR, ad esempio 192.168.1.0/24"),
|
||||
("Continue", "Continua"),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "中継接続"),
|
||||
("Secure Connection", "安全な接続"),
|
||||
("Insecure Connection", "安全でない接続"),
|
||||
("Continue", ""),
|
||||
("Scale original", "オリジナルのサイズ"),
|
||||
("Scale adaptive", "ウィンドウに合わせる"),
|
||||
("General", "一般"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "あなたの IP アドレスは接続先によってブロックされています"),
|
||||
("id_whitelist_caveat_tip", "ID は接続するクライアントから申告されます。ホワイトリストは露出を減らすもので、パスワードや 2FA の代わりにはなりません"),
|
||||
("whitelist_cidr_tip", "CIDR 表記に対応しています。例: 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -773,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "귀하의 IP가 상대방에 의해 차단되었습니다"),
|
||||
("id_whitelist_caveat_tip", "ID는 연결하는 클라이언트가 보고합니다. 화이트리스트는 노출을 줄이는 것으로 비밀번호나 2FA를 대체하지 않습니다"),
|
||||
("whitelist_cidr_tip", "CIDR 표기를 지원합니다. 예: 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Релай Қосылым"),
|
||||
("Secure Connection", "Қауіпсіз Қосылым"),
|
||||
("Insecure Connection", "Қатерлі Қосылым"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Scale original"),
|
||||
("Scale adaptive", "Scale adaptive"),
|
||||
("General", "Жалпы"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Сіздің IP-мекенжайыңыз қарсы тараппен бұғатталған"),
|
||||
("id_whitelist_caveat_tip", "ID қосылатын клиентпен хабарланады. Ақ-тізім әсер ету аумағын азайтады және құпия сөзді немесе 2FA-ны алмастырмайды"),
|
||||
("whitelist_cidr_tip", "CIDR жазбасына қолдау көрсетіледі, мысалы 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Tarpinė jungtis"),
|
||||
("Secure Connection", "Saugus ryšys"),
|
||||
("Insecure Connection", "Nesaugus ryšys"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Pakeisti originalų mastelį"),
|
||||
("Scale adaptive", "Pritaikomas mastelis"),
|
||||
("General", "Bendra"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Jūsų IP adresą užblokavo nuotolinis įrenginys"),
|
||||
("id_whitelist_caveat_tip", "ID praneša prisijungiantis klientas. Šis sąrašas sumažina atakos paviršių ir nepakeičia slaptažodžio ar 2FA"),
|
||||
("whitelist_cidr_tip", "Palaikomas CIDR žymėjimas, pavyzdžiui 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Releja savienojums"),
|
||||
("Secure Connection", "Drošs savienojums"),
|
||||
("Insecure Connection", "Nedrošs savienojums"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Mērogs oriģināls"),
|
||||
("Scale adaptive", "Mērogs adaptīvs"),
|
||||
("General", "Vispārīgi"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Jūsu IP adresi ir bloķējusi otra puse"),
|
||||
("id_whitelist_caveat_tip", "ID paziņo klients, kas veido savienojumu. Baltais saraksts samazina pakļautību un neaizstāj paroli vai 2FA"),
|
||||
("whitelist_cidr_tip", "Tiek atbalstīts CIDR pieraksts, piemēram 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "റിലേ കണക്ഷൻ"),
|
||||
("Secure Connection", "സുരക്ഷിതമായ കണക്ഷൻ"),
|
||||
("Insecure Connection", "സുരക്ഷിതമല്ലാത്ത കണക്ഷൻ"),
|
||||
("Continue", ""),
|
||||
("Scale original", "ഒറിജിനൽ വലിപ്പം"),
|
||||
("Scale adaptive", "അഡാപ്റ്റീവ് വലിപ്പം"),
|
||||
("General", "പൊതുവായവ"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "നിങ്ങളുടെ IP വിലാസം മറുവശം ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"),
|
||||
("id_whitelist_caveat_tip", "കണക്റ്റ് ചെയ്യുന്ന ക്ലയന്റാണ് ID റിപ്പോർട്ട് ചെയ്യുന്നത്. വൈറ്റ്ലിസ്റ്റ് എക്സ്പോഷർ കുറയ്ക്കുന്നു; പാസ്വേഡിനോ 2FA-യ്ക്കോ പകരമല്ല"),
|
||||
("whitelist_cidr_tip", "CIDR നൊട്ടേഷൻ പിന്തുണയ്ക്കുന്നു, ഉദാഹരണത്തിന് 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Viderekoblet tilkobling"),
|
||||
("Secure Connection", "Sikker tilkobling"),
|
||||
("Insecure Connection", "Usikker tilkobling"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Original skalering"),
|
||||
("Scale adaptive", "Adaptiv skalering"),
|
||||
("General", "Generelt"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "IP-adressen din er blokkert av motparten"),
|
||||
("id_whitelist_caveat_tip", "ID-en rapporteres av klienten som kobler til. Hvitelisten reduserer eksponeringen og erstatter ikke passord eller 2FA"),
|
||||
("whitelist_cidr_tip", "CIDR-notasjon støttes, for eksempel 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Relay-verbinding"),
|
||||
("Secure Connection", "Beveiligde verbinding"),
|
||||
("Insecure Connection", "Onveilige verbinding"),
|
||||
("Continue", "Doorgaan"),
|
||||
("Scale original", "Oorspronkelijk formaat"),
|
||||
("Scale adaptive", "Automatisch schalen"),
|
||||
("General", "Algemeen"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Uw IP wordt door de ander geblokkeerd"),
|
||||
("id_whitelist_caveat_tip", "De ID wordt vermeld door de verbindende client. Deze witte lijst vermindert de zichtbaarheid en vervangt niet het wachtwoord of 2FA."),
|
||||
("whitelist_cidr_tip", "CIDR-notatie wordt ondersteund, bijv. 192.168.1.0/24"),
|
||||
("Continue", "Doorgaan"),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Połączenie przez bramkę"),
|
||||
("Secure Connection", "Połączenie szyfrowane"),
|
||||
("Insecure Connection", "Połączenie nieszyfrowane"),
|
||||
("Continue", "Kontynuuj"),
|
||||
("Scale original", "Skalowanie oryginalne"),
|
||||
("Scale adaptive", "Dopasuj do wyświetlacza"),
|
||||
("General", "Ogólne"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Twój adres IP został zablokowany przez drugą stronę"),
|
||||
("id_whitelist_caveat_tip", "ID jest zgłaszane przez łączącego się klienta. Biała lista zmniejsza ekspozycję i nie zastępuje hasła ani 2FA"),
|
||||
("whitelist_cidr_tip", "Obsługiwana jest notacja CIDR, na przykład 192.168.1.0/24"),
|
||||
("Continue", "Kontynuuj"),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Conexão de relé"),
|
||||
("Secure Connection", "Conexão segura"),
|
||||
("Insecure Connection", "Conexão insegura"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Escala original"),
|
||||
("Scale adaptive", "Escala adaptável"),
|
||||
("General", "Geral"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "O seu IP está bloqueado pelo dispositivo remoto"),
|
||||
("id_whitelist_caveat_tip", "O ID é comunicado pelo cliente que se liga. A whitelist reduz a exposição e não substitui a palavra-passe nem o 2FA"),
|
||||
("whitelist_cidr_tip", "A notação CIDR é suportada, por exemplo 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Conexão via Relay"),
|
||||
("Secure Connection", "Conexão Segura"),
|
||||
("Insecure Connection", "Conexão Insegura"),
|
||||
("Continue", "Continuar"),
|
||||
("Scale original", "Escala original"),
|
||||
("Scale adaptive", "Escala adaptada"),
|
||||
("General", "Geral"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Seu IP foi bloqueado pelo dispositivo remoto"),
|
||||
("id_whitelist_caveat_tip", "O ID é informado pelo cliente que se conecta. A lista reduz a exposição e não substitui a senha ou o 2FA"),
|
||||
("whitelist_cidr_tip", "A notação CIDR é suportada, por exemplo 192.168.1.0/24"),
|
||||
("Continue", "Continuar"),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Conexiune prin retransmisie"),
|
||||
("Secure Connection", "Conexiune securizată"),
|
||||
("Insecure Connection", "Conexiune nesecurizată"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Dimensiune originală"),
|
||||
("Scale adaptive", "Scalare automată"),
|
||||
("General", "General"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Adresa ta IP este blocată de dispozitivul de la distanță"),
|
||||
("id_whitelist_caveat_tip", "ID-ul este raportat de clientul care se conectează. Lista albă reduce expunerea și nu înlocuiește parola sau 2FA"),
|
||||
("whitelist_cidr_tip", "Notația CIDR este acceptată, de exemplu 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Ретранслируемое подключение"),
|
||||
("Secure Connection", "Безопасное подключение"),
|
||||
("Insecure Connection", "Небезопасное подключение"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Оригинальный масштаб"),
|
||||
("Scale adaptive", "Адаптивный масштаб"),
|
||||
("General", "Общие"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Ваш IP-адрес заблокирован удалённым устройством"),
|
||||
("id_whitelist_caveat_tip", "ID сообщается подключающимся клиентом. Белый список уменьшает поверхность атаки и не заменяет пароль или 2FA"),
|
||||
("whitelist_cidr_tip", "Поддерживается нотация CIDR, например 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Connessione tramudada (relay)"),
|
||||
("Secure Connection", "Connessione segura"),
|
||||
("Insecure Connection", "Connessione non segura"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Iscala originale"),
|
||||
("Scale adaptive", "Iscala adativa"),
|
||||
("General", "Generale"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "S'indiritzu IP tuo est blocadu dae s'àtera parte"),
|
||||
("id_whitelist_caveat_tip", "S'ID est decraradu dae su cliente chi si connetet. Custu elencu minimat s'espositzione e non sostituit sa crae o su 2FA"),
|
||||
("whitelist_cidr_tip", "Sa notatzione CIDR est suportada, pro esempru 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Reléové pripojenie"),
|
||||
("Secure Connection", "Zabezpečené pripojenie"),
|
||||
("Insecure Connection", "Nezabezpečené pripojenie"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Pôvodná mierka"),
|
||||
("Scale adaptive", "Prispôsobivá mierka"),
|
||||
("General", "Všeobecné"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Vaša IP adresa je blokovaná protistranou"),
|
||||
("id_whitelist_caveat_tip", "ID nahlasuje pripájajúci sa klient. Tento zoznam znižuje vystavenie a nenahrádza heslo ani 2FA"),
|
||||
("whitelist_cidr_tip", "Je podporovaný zápis CIDR, napríklad 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Posredovana povezava"),
|
||||
("Secure Connection", "Zavarovana povezava"),
|
||||
("Insecure Connection", "Nezavarovana povezava"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Originalna velikost"),
|
||||
("Scale adaptive", "Prilagojena velikost"),
|
||||
("General", "Splošno"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Vaš IP je blokirala oddaljena naprava"),
|
||||
("id_whitelist_caveat_tip", "ID sporoči odjemalec, ki se povezuje. Seznam zmanjšuje izpostavljenost in ne nadomešča gesla ali 2FA"),
|
||||
("whitelist_cidr_tip", "Podprt je zapis CIDR, na primer 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Lidhja rele"),
|
||||
("Secure Connection", "Lidhje e sigurt"),
|
||||
("Insecure Connection", "Lidhje e pasigurt"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Shkalla origjinale"),
|
||||
("Scale adaptive", " E përsjhtatshme në shkallë"),
|
||||
("General", "Gjeneral"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "IP-ja juaj është bllokuar nga pala tjetër"),
|
||||
("id_whitelist_caveat_tip", "ID-ja raportohet nga klienti që lidhet. Lista e bardhë zvogëlon ekspozimin dhe nuk zëvendëson fjalëkalimin ose 2FA"),
|
||||
("whitelist_cidr_tip", "Mbështetet shënimi CIDR, për shembull 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Posredna konekcija"),
|
||||
("Secure Connection", "Bezbedna konekcija"),
|
||||
("Insecure Connection", "Nebezbedna konekcija"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Skaliraj original"),
|
||||
("Scale adaptive", "Adaptivno skaliranje"),
|
||||
("General", "Uopšteno"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Vašu IP adresu je blokirala druga strana"),
|
||||
("id_whitelist_caveat_tip", "ID prijavljuje klijent koji se povezuje. Ova lista smanjuje izloženost i ne zamenjuje lozinku ni 2FA"),
|
||||
("whitelist_cidr_tip", "Podržan je CIDR zapis, na primer 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Relayanslutning"),
|
||||
("Secure Connection", "Säker anslutning"),
|
||||
("Insecure Connection", "Osäker anslutning"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Skala orginal"),
|
||||
("Scale adaptive", "Skala adaptivt"),
|
||||
("General", "Generellt"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Din IP-adress är blockerad av motparten"),
|
||||
("id_whitelist_caveat_tip", "ID:t rapporteras av klienten som ansluter. Vitlistan minskar exponeringen och ersätter inte lösenord eller 2FA"),
|
||||
("whitelist_cidr_tip", "CIDR-notation stöds, till exempel 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "ரிலே இணைப்பு"),
|
||||
("Secure Connection", "பாதுகாப்பான இணைப்பு"),
|
||||
("Insecure Connection", "பாதுகாப்பற்ற இணைப்பு"),
|
||||
("Continue", ""),
|
||||
("Scale original", "அசல் அளவு"),
|
||||
("Scale adaptive", "தகவமைப்பு அளவு"),
|
||||
("General", "பொது"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "உங்கள் IP முகவரி மறுமுனையால் தடுக்கப்பட்டுள்ளது"),
|
||||
("id_whitelist_caveat_tip", "இணைக்கும் கிளையண்டே ID-ஐ தெரிவிக்கிறது. அனுமதிப்பட்டியல் வெளிப்பாட்டைக் குறைக்கிறது; கடவுச்சொல் அல்லது 2FA-க்கு மாற்றாகாது"),
|
||||
("whitelist_cidr_tip", "CIDR குறியீடு ஆதரிக்கப்படுகிறது, எடுத்துக்காட்டாக 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", ""),
|
||||
("Secure Connection", ""),
|
||||
("Insecure Connection", ""),
|
||||
("Continue", ""),
|
||||
("Scale original", ""),
|
||||
("Scale adaptive", ""),
|
||||
("General", ""),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", ""),
|
||||
("id_whitelist_caveat_tip", ""),
|
||||
("whitelist_cidr_tip", ""),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "การเชื่อมต่อแบบ Relay "),
|
||||
("Secure Connection", "การเชื่อมต่อที่ปลอดภัย"),
|
||||
("Insecure Connection", "การเชื่อมต่อที่ไม่ปลอดภัย"),
|
||||
("Continue", ""),
|
||||
("Scale original", "ขนาดเดิม"),
|
||||
("Scale adaptive", "ขนาดยืดหยุ่น"),
|
||||
("General", "ทั่วไป"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "IP ของคุณถูกบล็อกโดยฝั่งตรงข้าม"),
|
||||
("id_whitelist_caveat_tip", "ID ถูกรายงานโดยไคลเอนต์ที่เชื่อมต่อ ไวท์ลิสต์ช่วยลดการเปิดเผยและไม่สามารถใช้แทนรหัสผ่านหรือ 2FA ได้"),
|
||||
("whitelist_cidr_tip", "รองรับรูปแบบ CIDR เช่น 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Aktarmalı Bağlantı"),
|
||||
("Secure Connection", "Güvenli Bağlantı"),
|
||||
("Insecure Connection", "Güvenli Olmayan Bağlantı"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Orijinal ölçekte"),
|
||||
("Scale adaptive", "Uyarlanabilir ölçekte"),
|
||||
("General", "Genel"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "IP adresiniz karşı taraf tarafından engellendi"),
|
||||
("id_whitelist_caveat_tip", "ID, bağlanan istemci tarafından bildirilir. Bu liste maruziyeti azaltır; parolanın veya 2FA'nın yerini tutmaz"),
|
||||
("whitelist_cidr_tip", "CIDR gösterimi desteklenir, örneğin 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "中繼連線"),
|
||||
("Secure Connection", "安全連線"),
|
||||
("Insecure Connection", "非安全連線"),
|
||||
("Continue", ""),
|
||||
("Scale original", "原始尺寸"),
|
||||
("Scale adaptive", "適應視窗"),
|
||||
("General", "一般"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "你的 IP 已被對方封鎖"),
|
||||
("id_whitelist_caveat_tip", "ID 由對端用戶端回報,白名單用於減少暴露面,不能取代密碼或 2FA"),
|
||||
("whitelist_cidr_tip", "支援 CIDR 寫法,例如 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Ретрансльоване підключення"),
|
||||
("Secure Connection", "Безпечне підключення"),
|
||||
("Insecure Connection", "Небезпечне підключення"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Оригінальний масштаб"),
|
||||
("Scale adaptive", "Адаптивний масштаб"),
|
||||
("General", "Загальні"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "Вашу IP-адресу заблоковано віддаленим пристроєм"),
|
||||
("id_whitelist_caveat_tip", "ID повідомляється клієнтом, що підключається. Білий список зменшує поверхню атаки і не замінює пароль або 2FA"),
|
||||
("whitelist_cidr_tip", "Підтримується нотація CIDR, наприклад 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
+2
-1
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relay Connection", "Kết nối chuyển tiếp"),
|
||||
("Secure Connection", "Kết nối bảo mật"),
|
||||
("Insecure Connection", "Kết nối không bảo mật"),
|
||||
("Continue", ""),
|
||||
("Scale original", "Tỷ lệ gốc"),
|
||||
("Scale adaptive", "Tỷ lệ thích ứng"),
|
||||
("General", "Chung"),
|
||||
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Your ip is blocked by the peer", "IP của bạn đã bị phía bên kia chặn"),
|
||||
("id_whitelist_caveat_tip", "ID do máy khách kết nối tự khai báo. Danh sách trắng giúp giảm mức độ lộ diện và không thay thế mật khẩu hay 2FA"),
|
||||
("whitelist_cidr_tip", "Hỗ trợ ký hiệu CIDR, ví dụ 192.168.1.0/24"),
|
||||
("Continue", ""),
|
||||
("Browser didn't open? Use the url below to sign in.", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user