diff --git a/Dockerfile b/Dockerfile index 72238b6..dfcb75d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN dart pub get COPY lib/ ./lib/ # Compile the application -RUN dart compile exe lib/waylume_server.dart -o waylume_server +RUN dart compile exe lib/main.dart -o waylume_server EXPOSE 3000 51820/udp diff --git a/lib/main.dart b/lib/main.dart index 29fdad6..1376a99 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,60 +2,26 @@ import 'dart:io'; -import 'package:dotenv/dotenv.dart'; -import 'package:http/http.dart' as http; import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart'; import 'package:shelf_router/shelf_router.dart'; -import 'package:supabase/supabase.dart'; -import 'package:waylume_server/supabase_heartbeat.dart'; -import 'package:waylume_server/utils.dart'; - -var env = DotEnv()..load(); - -dynamic fromEnivronment(String key) { - if (kIsRunningInDocker) { - return Platform.environment[key]; - } else { - return env[key]; - } -} - -final SupabaseClient SUPABASE_CLIENT = SupabaseClient( - "https://lsdrctuvnwdrzrdyoqzu.supabase.co", - fromEnivronment('SUPABASE_KEY')! -); - +import 'package:waylume_server/services/supabase_heartbeat.dart'; +import 'package:waylume_server/services/server_service.dart'; +import 'package:waylume_server/services/wireguard_service.dart'; +import 'package:waylume_server/core/utils.dart'; +import 'package:waylume_server/web/peer_routes.dart'; +List routers = [ + PeerRoutes().router, +]; void main() async { - if (kIsRunningInDocker) { print('Running in Docker environment.'); } else { print('Not running in Docker environment.'); } - String ip = await getWanIp(); - - // Check if SERVER_ID already exists in the database - var existsCheck = await SUPABASE_CLIENT - .from("waylume_servers") - .select() - .eq("id", fromEnivronment('SERVER_ID')) - .eq("host_ip", ip); - - if (existsCheck.isEmpty) { - await SUPABASE_CLIENT - .from("waylume_servers") - .insert({ - "id": fromEnivronment('SERVER_ID')!, - "last_heartbeat": DateTime.now().toUtc().toIso8601String(), - "host_ip": ip, - }); - } - - // Verify that WireGuard is installed if (!await isWireguardInstalled()) { print('WireGuard is not installed. Please install WireGuard to run this server.'); exit(1); @@ -63,28 +29,23 @@ void main() async { print('WireGuard is installed.'); } - // Start the heartbeat to keep the server alive + await WireGuardService.initializeServer(); + await ServerService.registerServer(); initHeartbeat(); - // Isolate peers to block traffic between them if (!Platform.isMacOS) { - await isolatePeers(); + await ServerService.isolatePeers(); } Router app = Router(); - app.get('/', (request) { return Response.ok('Welcome to Waylume Server!'); }); + for (var router in routers) { + app.mount('/api/', router); + } + var server = await serve(app, '0.0.0.0', 3000); print('Server running on http://${server.address.host}:${server.port}'); -} - -Future isolatePeers() async { - // Block traffic between peers (peer-to-peer isolation) - await Process.run('iptables', ['-I', 'FORWARD', '-i', 'wg0', '-o', 'wg0', '-j', 'DROP']); - - // Allow established/related connections (for return traffic) - await Process.run('iptables', ['-I', 'FORWARD', '-i', 'wg0', '-o', 'wg0', '-m', 'state', '--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']); } \ No newline at end of file diff --git a/lib/supabase_heartbeat.dart b/lib/supabase_heartbeat.dart deleted file mode 100644 index 3f9ac8a..0000000 --- a/lib/supabase_heartbeat.dart +++ /dev/null @@ -1,35 +0,0 @@ - -// Run a heartbeat to let supabase know that the client is still active. - -import 'dart:io'; -import 'dart:isolate'; -import 'dart:math'; - -import 'package:supabase/supabase.dart'; -import 'package:waylume_server/main.dart'; - -void initHeartbeat() { - - // Run this on a separate thread. - Isolate.spawn((_) async { - - // To avoid server deadlock - await Future.delayed(Duration(seconds: Random().nextInt(5))); - - while (true) { - DateTime now = DateTime.now().toUtc(); - - await SUPABASE_CLIENT - .from("waylume_servers") - .update({ "last_heartbeat": now.toIso8601String() }) - .eq("id", fromEnivronment("SERVER_ID")!); - - print("Heartbeat sent to Supabase at ${now.toIso8601String()}"); - - // Wait 30 seconds before sending the next heartbeat - await Future.delayed(Duration(seconds: 30)); - } - - }, null); - -} \ No newline at end of file diff --git a/lib/utils.dart b/lib/utils.dart deleted file mode 100644 index bb764bc..0000000 --- a/lib/utils.dart +++ /dev/null @@ -1,29 +0,0 @@ - -import 'dart:convert'; -import 'dart:io'; -import 'package:http/http.dart' as http; - -/// Get the current WAN IP address of the device. -Future getWanIp() async { - // Get the IP address of the server. https://api.ipify.org?format=json - var ipResponse = await http.get(Uri.parse('https://api.ipify.org?format=json')); - if (ipResponse.statusCode == 200) { - var ipData = jsonDecode(ipResponse.body); - return ipData['ip'] as String; - } else { - throw Exception('Failed to get server IP address'); - } -} - -/// Check if Wireguard is installed on the system. -Future isWireguardInstalled() async { - try { - var result = await Process.run('wg', ['--version']); - return result.exitCode == 0; - } catch (e) { - print('Error checking WireGuard installation: $e'); - return false; - } -} - -bool get kIsRunningInDocker => File('/.dockerenv').existsSync(); \ No newline at end of file diff --git a/lib/wireguard/peers.dart b/lib/wireguard/peers.dart index eaee83c..f8b07ec 100644 --- a/lib/wireguard/peers.dart +++ b/lib/wireguard/peers.dart @@ -2,8 +2,9 @@ import 'dart:convert'; import 'dart:io'; -import 'package:waylume_server/utils.dart'; +import 'package:waylume_server/core/utils.dart'; import 'package:waylume_server/wireguard/utils.dart'; +import 'package:waylume_server/wireguard/traffic_control.dart'; class WireGuardPeer { final String privateKey; @@ -49,16 +50,14 @@ PersistentKeepalive = 25 } } -Future generateClientConfig(WireGuardPeer peer) async { - - String server_endpoint = '${await getWanIp()}:51820'; // Assuming default WireGuard port +Future generateClientConfigForPeer(WireGuardPeer peer) async { + String serverEndpoint = '${await getWanIp()}:51820'; String serverPublicKey = await getServerPublicKey(); return peer.generateClientConfig( serverPublicKey: serverPublicKey, - serverEndpoint: server_endpoint, + serverEndpoint: serverEndpoint, ); - } /// Creates a new WireGuard peer and returns the peer info @@ -99,24 +98,15 @@ Future deletePeer(String publicKey) async { Future setSpeedLimit(String publicKey, int speedKbps) async { final peerIP = await _getPeerIP(publicKey); if (peerIP == null) throw Exception('Peer not found'); - - final mark = _getMarkForIP(peerIP); - - await Process.run('iptables', ['-I', 'FORWARD', '-s', peerIP, '-j', 'MARK', '--set-mark', mark.toString()]); - await Process.run('iptables', ['-I', 'FORWARD', '-d', peerIP, '-j', 'MARK', '--set-mark', mark.toString()]); - await Process.run('tc', ['class', 'add', 'dev', 'wg0', 'parent', '1:1', 'classid', '1:$mark', 'htb', 'rate', '${speedKbps}kbit', 'ceil', '${speedKbps}kbit']); - await Process.run('tc', ['filter', 'add', 'dev', 'wg0', 'protocol', 'ip', 'parent', '1:', 'prio', '1', 'handle', mark.toString(), 'fw', 'flowid', '1:$mark']); + + await TrafficControlService.setSpeedLimit(peerIP, speedKbps); } Future setDataCap(String publicKey, int dataCapMB) async { final peerIP = await _getPeerIP(publicKey); if (peerIP == null) throw Exception('Peer not found'); - - final quotaBytes = dataCapMB * 1024 * 1024; - await Process.run('iptables', ['-I', 'FORWARD', '-s', peerIP, '-m', 'quota', '--quota', quotaBytes.toString(), '-j', 'ACCEPT']); - await Process.run('iptables', ['-I', 'FORWARD', '-d', peerIP, '-m', 'quota', '--quota', quotaBytes.toString(), '-j', 'ACCEPT']); - await Process.run('iptables', ['-A', 'FORWARD', '-s', peerIP, '-j', 'DROP']); - await Process.run('iptables', ['-A', 'FORWARD', '-d', peerIP, '-j', 'DROP']); + + await TrafficControlService.setDataCap(peerIP, dataCapMB); } Future _getPeerIP(String publicKey) async { @@ -133,10 +123,6 @@ Future _getPeerIP(String publicKey) async { return null; } -int _getMarkForIP(String ip) { - return ip.split('.').last.hashCode % 65535 + 1; -} - Future _getNextIP() async { final result = await Process.run('wg', ['show', 'wg0', 'allowed-ips']); final usedIPs = RegExp(r'10\.(\d+)\.(\d+)\.(\d+)') @@ -155,22 +141,3 @@ Future _getNextIP() async { } throw Exception('No available IPs'); } - -void main() async { - try { - final peer = await createPeer(); - print('New WireGuard Peer Created:'); - print('Private Key: ${peer.privateKey}'); - print('Public Key: ${peer.publicKey}'); - print('IP Address: ${peer.ip}'); - - // Example usage of generating client config - final clientConfig = peer.generateClientConfig( - serverPublicKey: 'your_server_public_key', - serverEndpoint: 'your_server_endpoint:51820', - ); - print('Client Config:\n$clientConfig'); - } catch (e) { - print('Error creating WireGuard peer: $e'); - } -} \ No newline at end of file