// Run a heartbeat to let supabase know that the client is still active. import 'dart:io'; import 'dart:isolate'; import 'dart:math'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:waylume_server/config/supabase_config.dart'; import 'package:waylume_server/services/rolling_codes_service.dart'; import 'package:waylume_server/core/utils.dart'; void initHeartbeat(String operationalSeed) { // Run this on a separate thread. Isolate.spawn((String seed) async { // Initialize rolling codes service in this isolate await RollingCodesService.initialize(); // Set the operational seed that was passed from main isolate RollingCodesService.setOperationalSeed(seed); // To avoid server deadlock and wait for main registration await Future.delayed(Duration(seconds: 10 + Random().nextInt(5))); while (true) { DateTime now = DateTime.now().toUtc(); try { // Wait until rolling codes service has operational seed if (!RollingCodesService.isRegistered) { print("Server not registered yet, skipping heartbeat"); await Future.delayed(Duration(seconds: 10)); // Check more frequently during startup continue; } // Generate operational rolling code for heartbeat authentication String authCode = RollingCodesService.generateOperationalCode(); // Get basic server status (only active connections) Map serverStatus = await _getServerStatus(); // Send heartbeat to server-manager endpoint String serverManagerUrl = 'https://lsdrctuvnwdrzrdyoqzu.supabase.co/functions/v1/server-manager'; Map heartbeatData = { 'server_id': fromEnivronment("SERVER_ID")!, 'timestamp': now.toIso8601String(), 'auth_code': authCode, 'status': serverStatus }; final response = await http.post( Uri.parse(serverManagerUrl), headers: { 'Content-Type': 'application/json', }, body: jsonEncode(heartbeatData), ); if (response.statusCode == 200) { final responseData = jsonDecode(response.body); if (responseData['success']) { print("Heartbeat sent successfully at ${now.toIso8601String()}"); } else { print("Heartbeat failed: ${responseData['error']}"); } } else { print("Heartbeat request failed: ${response.statusCode}"); } } catch (e) { print("Error sending heartbeat: $e"); } // Wait 90 seconds (1.5 minutes) before sending the next heartbeat await Future.delayed(Duration(seconds: 90)); } }, operationalSeed); } Future> _getServerStatus() async { int activeConnections = 0; try { // Get active WireGuard connections count ProcessResult wgShowResult = await Process.run('wg', ['show', 'wg0']); if (wgShowResult.exitCode == 0) { // Count peer entries (simplified - each peer has multiple lines) String output = wgShowResult.stdout as String; activeConnections = 'peer:'.allMatches(output).length; } } catch (e) { print("Error getting WireGuard status: $e"); } return { 'active_connections': activeConnections, }; }