Skip to content

Commit 8fa8798

Browse files
committed
feat(login): better login(server indicate+WSS+auto tcp)
1 parent c47626b commit 8fa8798

12 files changed

Lines changed: 492 additions & 55 deletions

lib/constants/app_constants.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ class AppConstants {
2626
static const int defaultApiPort = 8080;
2727
static const int defaultTcpPort = 9090;
2828
static const bool defaultUseHttps = false;
29+
static const bool defaultTryWss = false;
30+
static const bool defaultAutoDetectTcpPort = true;
2931
}

lib/l10n/app_en.arb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,14 @@
194194
"serverErrorInvalidPort": "Port must be an integer between 0 and 65535",
195195
"serverErrorDuplicatePort": "Ports cannot be the same",
196196
"serverUseHttps": "HTTPS",
197-
"serverUseHttpsOn": "Use encrypted connection",
197+
"serverUseHttpsOn": "Try encrypted connection (falls back to HTTP)",
198198
"serverUseHttpsOff": "Use unencrypted connection",
199+
"serverSave": "Save",
200+
"serverTryWss": "WSS",
201+
"serverTryWssOn": "Try secure WebSocket (falls back to WS)",
202+
"serverTryWssOff": "Use unencrypted WebSocket",
203+
"serverAutoDetectTcpPort": "Auto-detect TCP Port",
204+
"serverAutoDetectTcpPortDesc": "Fetch the TCP port from the server automatically",
199205

200206
"navChat": "Chat",
201207
"navAnnouncement": "Announce",

lib/l10n/app_localizations.dart

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ abstract class AppLocalizations {
10511051
/// No description provided for @serverUseHttpsOn.
10521052
///
10531053
/// In en, this message translates to:
1054-
/// **'Use encrypted connection'**
1054+
/// **'Try encrypted connection (falls back to HTTP)'**
10551055
String get serverUseHttpsOn;
10561056

10571057
/// No description provided for @serverUseHttpsOff.
@@ -1060,6 +1060,42 @@ abstract class AppLocalizations {
10601060
/// **'Use unencrypted connection'**
10611061
String get serverUseHttpsOff;
10621062

1063+
/// No description provided for @serverSave.
1064+
///
1065+
/// In en, this message translates to:
1066+
/// **'Save'**
1067+
String get serverSave;
1068+
1069+
/// No description provided for @serverTryWss.
1070+
///
1071+
/// In en, this message translates to:
1072+
/// **'WSS'**
1073+
String get serverTryWss;
1074+
1075+
/// No description provided for @serverTryWssOn.
1076+
///
1077+
/// In en, this message translates to:
1078+
/// **'Try secure WebSocket (falls back to WS)'**
1079+
String get serverTryWssOn;
1080+
1081+
/// No description provided for @serverTryWssOff.
1082+
///
1083+
/// In en, this message translates to:
1084+
/// **'Use unencrypted WebSocket'**
1085+
String get serverTryWssOff;
1086+
1087+
/// No description provided for @serverAutoDetectTcpPort.
1088+
///
1089+
/// In en, this message translates to:
1090+
/// **'Auto-detect TCP Port'**
1091+
String get serverAutoDetectTcpPort;
1092+
1093+
/// No description provided for @serverAutoDetectTcpPortDesc.
1094+
///
1095+
/// In en, this message translates to:
1096+
/// **'Fetch the TCP port from the server automatically'**
1097+
String get serverAutoDetectTcpPortDesc;
1098+
10631099
/// No description provided for @navChat.
10641100
///
10651101
/// In en, this message translates to:

lib/l10n/app_localizations_en.dart

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,11 +513,31 @@ class AppLocalizationsEn extends AppLocalizations {
513513
String get serverUseHttps => 'HTTPS';
514514

515515
@override
516-
String get serverUseHttpsOn => 'Use encrypted connection';
516+
String get serverUseHttpsOn =>
517+
'Try encrypted connection (falls back to HTTP)';
517518

518519
@override
519520
String get serverUseHttpsOff => 'Use unencrypted connection';
520521

522+
@override
523+
String get serverSave => 'Save';
524+
525+
@override
526+
String get serverTryWss => 'WSS';
527+
528+
@override
529+
String get serverTryWssOn => 'Try secure WebSocket (falls back to WS)';
530+
531+
@override
532+
String get serverTryWssOff => 'Use unencrypted WebSocket';
533+
534+
@override
535+
String get serverAutoDetectTcpPort => 'Auto-detect TCP Port';
536+
537+
@override
538+
String get serverAutoDetectTcpPortDesc =>
539+
'Fetch the TCP port from the server automatically';
540+
521541
@override
522542
String get navChat => 'Chat';
523543

lib/l10n/app_localizations_och.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,11 +486,29 @@ class AppLocalizationsOch extends AppLocalizations {
486486
String get serverUseHttps => 'HTTPS';
487487

488488
@override
489-
String get serverUseHttpsOn => '以加密連接';
489+
String get serverUseHttpsOn => '試以加密連接(敗則退HTTP)';
490490

491491
@override
492492
String get serverUseHttpsOff => '以明文連接';
493493

494+
@override
495+
String get serverSave => '存';
496+
497+
@override
498+
String get serverTryWss => 'WSS';
499+
500+
@override
501+
String get serverTryWssOn => '試以加密WebSocket(敗則退WS)';
502+
503+
@override
504+
String get serverTryWssOff => '以明文WebSocket';
505+
506+
@override
507+
String get serverAutoDetectTcpPort => '自探TCP端口';
508+
509+
@override
510+
String get serverAutoDetectTcpPortDesc => '自服器取TCP端口';
511+
494512
@override
495513
String get navChat => '聊天';
496514

lib/l10n/app_localizations_zh.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,11 +486,29 @@ class AppLocalizationsZh extends AppLocalizations {
486486
String get serverUseHttps => 'HTTPS';
487487

488488
@override
489-
String get serverUseHttpsOn => '使用加密连接';
489+
String get serverUseHttpsOn => '尝试加密连接(失败时回退 HTTP)';
490490

491491
@override
492492
String get serverUseHttpsOff => '使用非加密连接';
493493

494+
@override
495+
String get serverSave => '保存';
496+
497+
@override
498+
String get serverTryWss => 'WSS';
499+
500+
@override
501+
String get serverTryWssOn => '尝试加密 WebSocket(失败时回退 WS)';
502+
503+
@override
504+
String get serverTryWssOff => '使用非加密 WebSocket';
505+
506+
@override
507+
String get serverAutoDetectTcpPort => '自动检测TCP端口';
508+
509+
@override
510+
String get serverAutoDetectTcpPortDesc => '自动从服务器获取TCP端口';
511+
494512
@override
495513
String get navChat => '聊天';
496514

lib/l10n/app_och.arb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,14 @@
180180
"serverErrorInvalidPort": "端口須為0至65535間之整數",
181181
"serverErrorDuplicatePort": "端口不可複",
182182
"serverUseHttps": "HTTPS",
183-
"serverUseHttpsOn": "以加密連接",
183+
"serverUseHttpsOn": "試以加密連接(敗則退HTTP)",
184184
"serverUseHttpsOff": "以明文連接",
185+
"serverSave": "存",
186+
"serverTryWss": "WSS",
187+
"serverTryWssOn": "試以加密WebSocket(敗則退WS)",
188+
"serverTryWssOff": "以明文WebSocket",
189+
"serverAutoDetectTcpPort": "自探TCP端口",
190+
"serverAutoDetectTcpPortDesc": "自服器取TCP端口",
185191

186192
"navChat": "聊天",
187193
"navAnnouncement": "公告",

lib/l10n/app_zh.arb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,14 @@
180180
"serverErrorInvalidPort": "端口需为0到65535间的整数",
181181
"serverErrorDuplicatePort": "端口不能重复",
182182
"serverUseHttps": "HTTPS",
183-
"serverUseHttpsOn": "使用加密连接",
183+
"serverUseHttpsOn": "尝试加密连接(失败时回退 HTTP)",
184184
"serverUseHttpsOff": "使用非加密连接",
185+
"serverSave": "保存",
186+
"serverTryWss": "WSS",
187+
"serverTryWssOn": "尝试加密 WebSocket(失败时回退 WS)",
188+
"serverTryWssOff": "使用非加密 WebSocket",
189+
"serverAutoDetectTcpPort": "自动检测TCP端口",
190+
"serverAutoDetectTcpPortDesc": "自动从服务器获取TCP端口",
185191

186192
"navChat": "聊天",
187193
"navAnnouncement": "公告",

lib/services/api/tf_api_client.dart

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -303,29 +303,93 @@ class TfApiClient {
303303
}
304304
}
305305

306-
Future<String> getBaseUrl() async {
307-
if (_cachedBaseUrl != null) return _cachedBaseUrl!;
306+
Future<bool> _canReachUrl(
307+
String url, {
308+
Duration timeout = _probeTimeout,
309+
}) async {
310+
try {
311+
final response = await _http.get(Uri.parse(url)).timeout(timeout);
312+
return response.statusCode == 200;
313+
} catch (_) {
314+
return false;
315+
}
316+
}
317+
318+
/// probe!
319+
Future<bool> probeServer(ServerInfo? server) async {
320+
try {
321+
final baseUrl = await _resolveBaseUrlFor(server);
322+
return await _canReachUrl('$baseUrl/info');
323+
} catch (_) {
324+
return false;
325+
}
326+
}
327+
328+
Future<bool> probeConnection() async {
329+
try {
330+
final baseUrl = await getBaseUrl();
331+
return await _canReachUrl('$baseUrl/info');
332+
} catch (_) {
333+
return false;
334+
}
335+
}
336+
337+
Future<ServerInfo?> _loadSelectedServer() async {
308338
final prefs = await SharedPreferences.getInstance();
309339
final serversJson = prefs.getStringList('serversV2');
310340
final selectedIndex = prefs.getInt('selectedServerIndex') ?? 0;
311-
312341
if (serversJson != null && serversJson.isNotEmpty) {
313342
final idx = selectedIndex.clamp(0, serversJson.length - 1);
314-
final serverInfo = ServerInfo.fromJson(jsonDecode(serversJson[idx]));
315-
final port = serverInfo.apiPort.isNotEmpty
316-
? serverInfo.apiPort
317-
: AppConstants.defaultApiPort.toString();
318-
final scheme = serverInfo.useHttps ? 'https' : 'http';
319-
_cachedBaseUrl = '$scheme://${serverInfo.address}:$port';
320-
return _cachedBaseUrl!;
343+
return ServerInfo.fromJson(jsonDecode(serversJson[idx]));
321344
}
322-
final defaultScheme =
323-
AppConstants.defaultUseHttps ? 'https' : 'http';
324-
_cachedBaseUrl =
325-
'$defaultScheme://${AppConstants.defaultServerAddress}:${AppConstants.defaultApiPort}';
345+
return null;
346+
}
347+
348+
Future<String> _resolveBaseUrlFor(ServerInfo? server) async {
349+
final address = (server != null && server.address.isNotEmpty)
350+
? server.address
351+
: AppConstants.defaultServerAddress;
352+
final port = (server != null && server.apiPort.isNotEmpty)
353+
? server.apiPort
354+
: AppConstants.defaultApiPort.toString();
355+
final useHttps = server?.useHttps ?? AppConstants.defaultUseHttps;
356+
357+
if (useHttps) {
358+
final httpsUrl = 'https://$address:$port';
359+
if (await _canReachUrl('$httpsUrl/info')) {
360+
return httpsUrl;
361+
}
362+
return 'http://$address:$port';
363+
}
364+
365+
return 'http://$address:$port';
366+
}
367+
368+
Future<String> getBaseUrl() async {
369+
if (_cachedBaseUrl != null) return _cachedBaseUrl!;
370+
final serverInfo = await _loadSelectedServer();
371+
_cachedBaseUrl = await _resolveBaseUrlFor(serverInfo);
326372
return _cachedBaseUrl!;
327373
}
328374

375+
376+
Future<int> resolveTcpPort() async {
377+
final serverInfo = await _loadSelectedServer();
378+
final autoDetect =
379+
serverInfo?.autoDetectTcpPort ?? AppConstants.defaultAutoDetectTcpPort;
380+
if (autoDetect) {
381+
final config = await fetchServerInfo();
382+
if (config != null) return config.portTcp;
383+
}
384+
final raw = serverInfo?.tcpPort ?? '';
385+
return int.tryParse(raw) ?? AppConstants.defaultTcpPort;
386+
}
387+
388+
Future<bool> shouldTryWss() async {
389+
final serverInfo = await _loadSelectedServer();
390+
return serverInfo?.tryWss ?? AppConstants.defaultTryWss;
391+
}
392+
329393
void invalidateCache() {
330394
_cachedPubKey = null;
331395
_cachedBaseUrl = null;

lib/services/chat_ws_service.dart

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ class ChatWsService extends ChangeNotifier {
5757

5858
try {
5959
final host = await _resolveHost();
60-
final tcpPort = await _resolveTcpPort();
61-
for (final uri in candidateWebSocketUris(host, tcpPort)) {
60+
final tcpPort = await TfApiClient.instance.resolveTcpPort();
61+
final tryWss = await TfApiClient.instance.shouldTryWss();
62+
for (final uri in candidateWebSocketUris(host, tcpPort, tryWss: tryWss)) {
6263
final result = await _connectToCandidate(uri, uid, password);
6364
if (result == _CandidateConnectionResult.authenticated) {
6465
return true;
@@ -82,8 +83,12 @@ class ChatWsService extends ChangeNotifier {
8283
}
8384

8485
@visibleForTesting
85-
static List<Uri> candidateWebSocketUris(String host, int port) => [
86-
Uri(scheme: 'wss', host: host, port: port),
86+
static List<Uri> candidateWebSocketUris(
87+
String host,
88+
int port, {
89+
bool tryWss = true,
90+
}) => [
91+
if (tryWss) Uri(scheme: 'wss', host: host, port: port),
8792
Uri(scheme: 'ws', host: host, port: port),
8893
];
8994

@@ -298,19 +303,6 @@ class ChatWsService extends ChangeNotifier {
298303
return '127.0.0.1';
299304
}
300305

301-
Future<int> _resolveTcpPort() async {
302-
final prefs = await SharedPreferences.getInstance();
303-
final serversJson = prefs.getStringList('serversV2');
304-
final selectedIndex = prefs.getInt('selectedServerIndex') ?? 0;
305-
if (serversJson != null && serversJson.isNotEmpty) {
306-
final idx = selectedIndex.clamp(0, serversJson.length - 1);
307-
final info = jsonDecode(serversJson[idx]) as Map<String, dynamic>;
308-
final raw = info['tcpPort'] ?? info['tcp_port'];
309-
if (raw != null) return int.tryParse(raw.toString()) ?? 1145;
310-
}
311-
return 1145;
312-
}
313-
314306
void _startPing() {
315307
_pingTimer?.cancel();
316308
_pingTimer = Timer.periodic(const Duration(seconds: 50), (_) {

0 commit comments

Comments
 (0)