import 'dart:convert'; import 'dart:io'; import 'package:supabase/supabase.dart'; import 'package:waylume_server/config/supabase_config.dart'; class VpnSessionService { /// Detects existing VPN sessions for this server and prints their IDs with keepalive info static Future detectSessions() async { try { final serverId = fromEnivronment('SERVER_ID'); if (serverId == null) { print('ERROR: SERVER_ID environment variable not set'); return; } // Get all sessions for this server final sessions = await SUPABASE_CLIENT .from('vpn_sessions') .select('id, peer_info') .eq('server_id', serverId); if (sessions.isEmpty) { print('No VPN sessions found for server: $serverId'); } else { print('Found ${sessions.length} VPN sessions for server: $serverId'); for (final session in sessions) { final sessionId = session['id']; final peerInfo = session['peer_info']; if (peerInfo != null && peerInfo['peer'] != null && peerInfo['peer']['publicKey'] != null) { final publicKey = peerInfo['peer']['publicKey'] as String; final keepaliveInfo = await _getKeepaliveForPeer(publicKey); print('Session ID: $sessionId - Peer: ${publicKey.substring(0, 8)}... - $keepaliveInfo'); } else { print('Session ID: $sessionId - No peer public key available'); } } } } catch (e) { print('Error detecting sessions: $e'); } } /// Generates public key from private key static Future _getPublicKeyFromPrivateKey(String privateKey) async { try { final pubProcess = await Process.start('wg', ['pubkey']); pubProcess.stdin.writeln(privateKey); await pubProcess.stdin.close(); final publicKey = await pubProcess.stdout.transform(utf8.decoder).join(); return publicKey.trim(); } catch (e) { return 'Error generating public key: $e'; } } /// Gets keepalive info for a specific peer static Future _getKeepaliveForPeer(String publicKey) async { try { final result = await Process.run('wg', ['show', 'wg0', 'dump']); if (result.exitCode != 0) { return 'Failed to get WireGuard info'; } final lines = result.stdout.toString().trim().split('\n'); // Skip first line (server info) and look for this peer for (int i = 1; i < lines.length; i++) { final line = lines[i].trim(); if (line.isEmpty) continue; final parts = line.split('\t'); if (parts.length >= 5 && parts[0] == publicKey) { final latestHandshake = parts[4]; final handshakeTime = int.tryParse(latestHandshake) ?? 0; if (handshakeTime == 0) { return 'No handshake yet'; } else { final handshakeDateTime = DateTime.fromMillisecondsSinceEpoch(handshakeTime * 1000); final now = DateTime.now(); final duration = now.difference(handshakeDateTime); // Check if connection is dead (more than 2 minutes 30 seconds) if (duration.inSeconds > 150) { return 'DEAD - Last handshake: ${handshakeDateTime.toLocal()} (${duration.inMinutes}m ${duration.inSeconds % 60}s ago)'; } else { return 'ALIVE - Last handshake: ${handshakeDateTime.toLocal()} (${duration.inSeconds}s ago)'; } } } } return 'Peer not found in WireGuard'; } catch (e) { return 'Error checking keepalive: $e'; } } }