deleting old safety checks and connect polish

This commit is contained in:
esefxdz committed 2026-07-26 10:41:30 +03:00
1 parent a863656758
commit a32252c56d
3 files changed
+55 -64

No files matched your search

+28 -54
View File
@@ -7,104 +7,82 @@ import 'package:revoltvpn/logic/app_config.dart';
import 'package:revoltvpn/logic/crypto_service.dart';
class HivemindService {
/// Nonce generated at the start of a connect cycle.
/// Sent to the server during session creation and verified in every poll
/// response so we never accidentally resume a stale session.
static String? _expectedNonce;
static int _currentCallId = 0;
/// Call this from disconnect to abort any in-progress polling.
static void cancel() {
_currentCallId++; // bump — any running loop sees a stale ID and exits
}
/// Stores a nonce so that fetchConfigWithPolling can verify it.
/// Called by AdManager.showAd() before showing a rewarded ad so the
/// subsequent poll accepts only the session created by *this* ad.
static void setExpectedNonce(String nonce) {
_expectedNonce = nonce;
}
/// Polls the Hivemind server until the session is ready or timeout occurs.
/// Returns a VLESS connection URL string.
///
/// [onAttempt] is called after each poll with (current, total) so the UI
/// can show progress (e.g. "Contacting server (3/15)…").
/// Tries to fetch the VLESS config from the server.
/// Only polls a few times — by the time the ad finishes, the SSV callback
/// has usually already fired.
static Future<String> fetchConfigWithPolling({
void Function(int attempt, int total)? onAttempt,
}) async {
final deviceId = await CryptoService.getDeviceId();
// ── Generate a connect nonce ──────────────────────────────────────
final callId = ++_currentCallId;
final nonce = '${Random().nextInt(0x7FFFFFFF)}-${DateTime.now().millisecondsSinceEpoch}';
_expectedNonce = nonce;
debugPrint('[HivemindService] Connect nonce: $nonce');
debugPrint('[HivemindService] Call #$callId — nonce: $nonce');
final url = Uri.parse('${AppConfig.hivemindApiBase}/session/status?device_id=$deviceId');
// ── AD BYPASS (debug only) ────────────────────────────────────────
if (kDebugMode) {
try {
final customData = jsonEncode({
'device_id': deviceId,
'nonce': nonce,
});
final fakeAdmobPingUrl = Uri.parse(
final customData = jsonEncode({'device_id': deviceId, 'nonce': nonce});
final fakeUrl = Uri.parse(
'${AppConfig.hivemindApiBase}/admob/callback?signature=test&key_id=test&custom_data=${Uri.encodeComponent(customData)}');
await http.get(fakeAdmobPingUrl).timeout(const Duration(seconds: 8));
await http.get(fakeUrl).timeout(const Duration(seconds: 8));
} catch (_) {}
}
// ───────────────────────────────────────────────────────────────────
int attempts = 0;
while (attempts < 15) {
debugPrint('[HivemindService] Polling attempt ${attempts + 1}/15…');
onAttempt?.call(attempts + 1, 15);
const maxAttempts = 5;
for (int i = 1; i <= maxAttempts; i++) {
if (_currentCallId != callId) throw Exception('Cancelled');
onAttempt?.call(i, maxAttempts);
try {
final response = await http.get(url).timeout(const Duration(seconds: 10));
final response = await http.get(url).timeout(const Duration(seconds: 5));
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
debugPrint('[HivemindService] Session found, checking nonce…');
// ── Nonce gate ──────────────────────────────────────────────
final serverNonce = data['nonce'] as String?;
if (_expectedNonce != null && serverNonce != null && serverNonce != _expectedNonce) {
debugPrint('[HivemindService] Nonce mismatch (expected $_expectedNonce, got $serverNonce) — stale session, retrying…');
debugPrint('[HivemindService] Nonce mismatch — retrying…');
} else if (data['active'] == true && data['vless_uuid'] != null) {
final vlessUuid = data['vless_uuid'];
// Build VLESS connection URL
final vlessUrl = 'vless://$vlessUuid@${AppConfig.serverDomain}:${AppConfig.serverPort}'
'?path=${Uri.encodeComponent(AppConfig.vlessPath)}'
'&security=${AppConfig.vlessSecurity}'
'&type=${AppConfig.vlessType}'
'#ReVoltVPN';
_expectedNonce = null; // consumed successfully
_expectedNonce = null;
return vlessUrl;
} else if (data['active'] == true && data['client_ip'] != null) {
// Server is still running AWG — VLESS client cannot use this.
debugPrint('[HivemindService] Server returned AWG format (client_ip) — server migration needed.');
throw Exception('Server is running the old AWG protocol. '
'Please update the server to VLESS.');
}
} else {
debugPrint('[HivemindService] Non-200 status: ${response.statusCode}');
}
} catch (e) {
debugPrint('[HivemindService] Polling exception: $e');
// Ignore network errors and keep polling
if (e.toString().contains('Cancelled')) rethrow;
debugPrint('[HivemindService] Attempt $i failed: $e');
}
attempts++;
await Future.delayed(const Duration(seconds: 2));
if (i < maxAttempts) await Future.delayed(const Duration(seconds: 1));
}
throw Exception("Session not activated. S2S callback failed or timed out.");
throw Exception('Session not activated. Server callback may have timed out.');
}
/// Quick one-shot check: does the server have a live session for this device?
static Future<bool> verifySession() async {
try {
final deviceId = await CryptoService.getDeviceId();
final url = Uri.parse(
'${AppConfig.hivemindApiBase}/session/status?device_id=$deviceId');
final url = Uri.parse('${AppConfig.hivemindApiBase}/session/status?device_id=$deviceId');
final response = await http.get(url).timeout(const Duration(seconds: 5));
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
@@ -114,7 +92,6 @@ class HivemindService {
return false;
}
/// Disconnects the session (cleans up on server)
static Future<void> disconnectAndCleanup() async {
final deviceId = await CryptoService.getDeviceId();
final url = Uri.parse('${AppConfig.hivemindApiBase}/session/stop');
@@ -124,9 +101,6 @@ class HivemindService {
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'device_id': deviceId}),
).timeout(const Duration(seconds: 3));
} catch (_) {
// Best effort cleanup
}
} catch (_) {}
}
}