Add Waylume Manifesto and update API routes for peer management
This commit is contained in:
@@ -1,26 +1,4 @@
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
import 'package:waylume_server/services/vpn_session_service.dart';
|
||||
|
||||
void initVpnSessionMonitor() {
|
||||
// Run this on a separate thread
|
||||
Isolate.spawn((_) async {
|
||||
|
||||
// To avoid server deadlock, add random delay
|
||||
await Future.delayed(Duration(seconds: Random().nextInt(10)));
|
||||
|
||||
print('VPN Session Monitor started');
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
await VpnSessionService.detectSessions();
|
||||
} catch (e) {
|
||||
print('Error in VPN session monitor: $e');
|
||||
}
|
||||
|
||||
// Check for sessions every 60 seconds
|
||||
await Future.delayed(Duration(seconds: 60));
|
||||
}
|
||||
|
||||
}, null);
|
||||
// VPN session monitoring disabled - peer management now handled via API endpoints
|
||||
print('VPN Session Monitor: Monitoring disabled - using API endpoints for peer management');
|
||||
}
|
||||
@@ -1,63 +1,98 @@
|
||||
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<void> detectSessions() async {
|
||||
/// Gets all peers currently on the WireGuard interface
|
||||
static Future<List<Map<String, dynamic>>> getAllLocalPeers() async {
|
||||
try {
|
||||
final serverId = fromEnivronment('SERVER_ID');
|
||||
final peers = <Map<String, dynamic>>[];
|
||||
|
||||
if (serverId == null) {
|
||||
print('ERROR: SERVER_ID environment variable not set');
|
||||
return;
|
||||
// Get all peer IPs and public keys
|
||||
final allowedIpsResult = await Process.run('wg', ['show', 'wg0', 'allowed-ips']);
|
||||
if (allowedIpsResult.exitCode != 0) {
|
||||
return peers;
|
||||
}
|
||||
|
||||
// Get all sessions for this server
|
||||
final sessions = await SUPABASE_CLIENT
|
||||
.from('vpn_sessions')
|
||||
.select('id, peer_info')
|
||||
.eq('server_id', serverId);
|
||||
final lines = allowedIpsResult.stdout.toString().trim().split('\n');
|
||||
|
||||
if (sessions.isEmpty) {
|
||||
print('No VPN sessions found for server: $serverId');
|
||||
} else {
|
||||
print('Found ${sessions.length} VPN sessions for server: $serverId');
|
||||
for (final line in lines) {
|
||||
if (line.trim().isEmpty) continue;
|
||||
|
||||
for (final session in sessions) {
|
||||
final sessionId = session['id'];
|
||||
final peerInfo = session['peer_info'];
|
||||
final parts = line.trim().split('\t');
|
||||
if (parts.length >= 2) {
|
||||
final publicKey = parts[0];
|
||||
final ipWithMask = parts[1];
|
||||
final ipAddress = ipWithMask.split('/')[0];
|
||||
|
||||
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');
|
||||
}
|
||||
final keepaliveInfo = await _getKeepaliveForPeer(publicKey);
|
||||
final peerData = _parseKeepaliveInfo(keepaliveInfo, publicKey, ipAddress);
|
||||
|
||||
peers.add(peerData);
|
||||
}
|
||||
}
|
||||
|
||||
return peers;
|
||||
} catch (e) {
|
||||
print('Error detecting sessions: $e');
|
||||
print('Error getting local peers: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates public key from private key
|
||||
static Future<String> _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 only inactive peers (no handshake in >150 seconds)
|
||||
static Future<List<Map<String, dynamic>>> getInactivePeers() async {
|
||||
final allPeers = await getAllLocalPeers();
|
||||
return allPeers.where((peer) => peer['status'] == 'DEAD').toList();
|
||||
}
|
||||
|
||||
/// Parses keepalive info string into structured data
|
||||
static Map<String, dynamic> _parseKeepaliveInfo(String keepaliveInfo, String publicKey, String ipAddress) {
|
||||
if (keepaliveInfo.startsWith('DEAD')) {
|
||||
final regex = RegExp(r'DEAD - Last handshake: ([^(]+) \((\d+)m (\d+)s ago\)');
|
||||
final match = regex.firstMatch(keepaliveInfo);
|
||||
|
||||
if (match != null) {
|
||||
final lastHandshake = match.group(1)?.trim();
|
||||
final minutes = int.tryParse(match.group(2) ?? '0') ?? 0;
|
||||
final seconds = int.tryParse(match.group(3) ?? '0') ?? 0;
|
||||
final totalMinutes = minutes + (seconds / 60).round();
|
||||
|
||||
return {
|
||||
'public_key': publicKey,
|
||||
'ip_address': ipAddress,
|
||||
'last_handshake': lastHandshake,
|
||||
'minutes_since_handshake': totalMinutes,
|
||||
'status': 'DEAD'
|
||||
};
|
||||
}
|
||||
} else if (keepaliveInfo.startsWith('ALIVE')) {
|
||||
final regex = RegExp(r'ALIVE - Last handshake: ([^(]+) \((\d+)s ago\)');
|
||||
final match = regex.firstMatch(keepaliveInfo);
|
||||
|
||||
if (match != null) {
|
||||
final lastHandshake = match.group(1)?.trim();
|
||||
final seconds = int.tryParse(match.group(2) ?? '0') ?? 0;
|
||||
final minutes = (seconds / 60).round();
|
||||
|
||||
return {
|
||||
'public_key': publicKey,
|
||||
'ip_address': ipAddress,
|
||||
'last_handshake': lastHandshake,
|
||||
'minutes_since_handshake': minutes,
|
||||
'status': 'ALIVE'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for other statuses
|
||||
return {
|
||||
'public_key': publicKey,
|
||||
'ip_address': ipAddress,
|
||||
'last_handshake': null,
|
||||
'minutes_since_handshake': null,
|
||||
'status': keepaliveInfo
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Gets keepalive info for a specific peer
|
||||
static Future<String> _getKeepaliveForPeer(String publicKey) async {
|
||||
|
||||
Reference in New Issue
Block a user