@@ -635,21 +646,80 @@ AllowedIPs = 0.0.0.0/0">
}
}
+ function vpnLog(msg, type) {
+ const log = document.getElementById('vpn-log');
+ const entries = document.getElementById('vpn-log-entries');
+ log.style.display = 'block';
+ const time = new Date().toLocaleTimeString();
+ const color = type === 'error' ? '#f44336' : type === 'success' ? '#4CAF50' : type === 'warn' ? '#FF9800' : '#ccc';
+ entries.innerHTML += '
[' + time + '] ' + esc(msg) + '
';
+ entries.scrollTop = entries.scrollHeight;
+ }
+
+ async function pollVpnStatus(tunnelId, maxAttempts) {
+ for (let i = 0; i < maxAttempts; i++) {
+ await new Promise(r => setTimeout(r, 1000));
+ try {
+ const res = await fetch('/api/vpn-status');
+ const s = await res.json();
+ vpnLog('Status: ' + s.state + (s.error ? ' - ' + s.error : '') + ' | Service: ' + (s.serviceRunning ? 'ja' : 'nein'));
+ if (s.state === 'CONNECTED') {
+ vpnLog('Tunnel erfolgreich verbunden!', 'success');
+ loadTunnels();
+ return true;
+ }
+ if (s.state === 'ERROR') {
+ vpnLog('Fehler: ' + (s.error || 'Unbekannt'), 'error');
+ loadTunnels();
+ return false;
+ }
+ } catch (e) {
+ vpnLog('Status-Abfrage fehlgeschlagen: ' + e.message, 'error');
+ }
+ }
+ vpnLog('Timeout - Tunnel hat sich nicht verbunden nach ' + maxAttempts + 's', 'warn');
+ loadTunnels();
+ return false;
+ }
+
async function connectTunnel(id) {
+ document.getElementById('vpn-log-entries').innerHTML = '';
+ vpnLog('Sende Verbindungsanfrage fuer Tunnel ' + id + '...');
try {
const res = await fetch('/api/tunnel/' + id + '/connect', { method: 'POST' });
+ vpnLog('HTTP Response: ' + res.status);
const data = await res.json();
- if (data.success) setTimeout(loadTunnels, 1000);
- else alert('Fehler: ' + data.error);
- } catch (e) { alert('Fehler: ' + e.message); }
+ if (data.config) {
+ vpnLog('Tunnel: ' + data.config.name);
+ vpnLog('Endpoint: ' + data.config.endpoint);
+ vpnLog('Addresses: ' + data.config.addresses);
+ vpnLog('DNS: ' + data.config.dns);
+ vpnLog('AllowedIPs: ' + data.config.allowedIPs);
+ vpnLog('Peers: ' + data.config.peers);
+ }
+ if (data.success) {
+ vpnLog('Service gestartet, warte auf Verbindung...');
+ await pollVpnStatus(id, 15);
+ } else {
+ vpnLog('Fehler vom Server: ' + data.error, 'error');
+ }
+ } catch (e) {
+ vpnLog('Request fehlgeschlagen: ' + e.message, 'error');
+ }
}
async function disconnectTunnel() {
+ document.getElementById('vpn-log-entries').innerHTML = '';
+ vpnLog('Sende Trennungsanfrage...');
try {
const res = await fetch('/api/disconnect', { method: 'POST' });
const data = await res.json();
- if (data.success) setTimeout(loadTunnels, 1000);
- } catch (e) { alert('Fehler: ' + e.message); }
+ vpnLog('Antwort: ' + JSON.stringify(data));
+ if (data.success) {
+ vpnLog('Getrennt.', 'success');
+ setTimeout(loadTunnels, 1000);
+ }
+ } catch (e) { vpnLog('Fehler: ' + e.message, 'error'); }
}
async function deleteTunnel(id, name) {
@@ -1200,20 +1270,39 @@ AllowedIPs = 0.0.0.0/0">
private fun handleConnectTunnel(output: PrintWriter, tunnelId: String) {
try {
+ // Tunnel-Config laden für Debug-Info
+ var configInfo = ""
+ runBlocking {
+ try {
+ val repository = TunnelRepository.getInstance(context)
+ val tunnel = repository.getTunnel(tunnelId)
+ if (tunnel != null) {
+ val endpoint = tunnel.peers.firstOrNull()?.endpoint ?: "N/A"
+ val addresses = tunnel.interface_.addresses.joinToString(", ")
+ val dns = tunnel.interface_.dns.joinToString(", ")
+ val allowedIPs = tunnel.peers.firstOrNull()?.allowedIPs?.joinToString(", ") ?: "N/A"
+ configInfo = ""","config":{"name":"${escapeJson(tunnel.name)}","endpoint":"${escapeJson(endpoint)}","addresses":"${escapeJson(addresses)}","dns":"${escapeJson(dns)}","allowedIPs":"${escapeJson(allowedIPs)}","peers":${tunnel.peers.size}}"""
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Config info load failed: ${e.message}")
+ }
+ Unit
+ }
+
// VPN Service direkt über Intent starten (funktioniert auch ohne laufende App)
val intent = Intent(context, WireGuardTunnelService::class.java).apply {
action = WireGuardTunnelService.ACTION_CONNECT
putExtra(WireGuardTunnelService.EXTRA_TUNNEL_ID, tunnelId)
}
- context.startForegroundService(intent)
+ ContextCompat.startForegroundService(context, intent)
// Auch Callback aufrufen falls gesetzt (für UI-Updates)
onConnectTunnel?.invoke(tunnelId)
- sendJson(output, """{"success":true,"message":"Verbindung wird aufgebaut..."}""")
+ sendJson(output, """{"success":true,"message":"Verbindung wird aufgebaut..."$configInfo}""")
Log.i(TAG, "Connect request: $tunnelId")
} catch (e: Exception) {
- sendJson(output, """{"success":false,"error":"${e.message}"}""")
+ sendJson(output, """{"success":false,"error":"${escapeJson(e.message ?: "Unknown error")}"}""")
}
}
@@ -1235,6 +1324,22 @@ AllowedIPs = 0.0.0.0/0">
}
}
+ private fun handleGetVpnStatus(output: PrintWriter) {
+ try {
+ val vpnService = WireGuardTunnelService.getInstance()
+ val status = vpnService?.status?.value
+ val serviceRunning = vpnService != null
+
+ if (status != null) {
+ sendJson(output, """{"serviceRunning":$serviceRunning,"tunnelId":"${status.tunnelId}","state":"${status.state.name}","error":${if (status.errorMessage != null) "\"${escapeJson(status.errorMessage)}\"" else "null"},"rxBytes":${status.rxBytes},"txBytes":${status.txBytes},"publicIp":${if (status.publicIp != null) "\"${status.publicIp}\"" else "null"}}""")
+ } else {
+ sendJson(output, """{"serviceRunning":$serviceRunning,"tunnelId":null,"state":"DISCONNECTED","error":null}""")
+ }
+ } catch (e: Exception) {
+ sendJson(output, """{"serviceRunning":false,"state":"UNKNOWN","error":"${escapeJson(e.message ?: "Unknown error")}"}""")
+ }
+ }
+
private fun handleGetTunnelForEdit(output: PrintWriter, tunnelId: String) {
runBlocking {
try {
@@ -1396,7 +1501,7 @@ AllowedIPs = 0.0.0.0/0">
action = WireGuardTunnelService.ACTION_CONNECT
putExtra(WireGuardTunnelService.EXTRA_TUNNEL_ID, tunnelId)
}
- context.startForegroundService(intent)
+ ContextCompat.startForegroundService(context, intent)
onConnectTunnel?.invoke(tunnelId)
}
diff --git a/app/src/main/java/de/hackernet/wireguardtv/web/WebServerService.kt b/app/src/main/java/de/hackernet/wireguardtv/web/WebServerService.kt
index 36a1b4d..afda6e4 100644
--- a/app/src/main/java/de/hackernet/wireguardtv/web/WebServerService.kt
+++ b/app/src/main/java/de/hackernet/wireguardtv/web/WebServerService.kt
@@ -91,6 +91,9 @@ class WebServerService : Service() {
when (intent?.action) {
ACTION_START -> {
+ // startForeground() sofort aufrufen (muss innerhalb 5s nach startForegroundService passieren)
+ val ip = webServer?.getDeviceIpAddress() ?: "?"
+ startForeground(NOTIFICATION_ID, createNotification(ip, 8080))
scope.launch {
startServer()
}
@@ -102,6 +105,8 @@ class WebServerService : Service() {
}
else -> {
// Default: Server starten (z.B. beim Boot)
+ val ip = webServer?.getDeviceIpAddress() ?: "?"
+ startForeground(NOTIFICATION_ID, createNotification(ip, 8080))
scope.launch {
startServer()
}
@@ -141,9 +146,9 @@ class WebServerService : Service() {
settings.webServerPassword
)
- // Foreground Notification starten bevor der Server läuft
+ // Notification mit korrektem Port aktualisieren
val ip = webServer?.getDeviceIpAddress() ?: "?"
- startForeground(NOTIFICATION_ID, createNotification(ip, settings.webServerPort))
+ updateNotification(ip, settings.webServerPort)
webServer?.start()
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..01e13ca
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.xml b/app/src/main/res/mipmap-hdpi/ic_launcher.xml
deleted file mode 100644
index a77a600..0000000
--- a/app/src/main/res/mipmap-hdpi/ic_launcher.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..58eab44
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.xml b/app/src/main/res/mipmap-mdpi/ic_launcher.xml
deleted file mode 100644
index a77a600..0000000
--- a/app/src/main/res/mipmap-mdpi/ic_launcher.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..0a826be
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.xml b/app/src/main/res/mipmap-xhdpi/ic_launcher.xml
deleted file mode 100644
index a77a600..0000000
--- a/app/src/main/res/mipmap-xhdpi/ic_launcher.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d699463
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.xml b/app/src/main/res/mipmap-xxhdpi/ic_launcher.xml
deleted file mode 100644
index a77a600..0000000
--- a/app/src/main/res/mipmap-xxhdpi/ic_launcher.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/reports/problems/problems-report.html b/build/reports/problems/problems-report.html
index 4dd69f9..783129e 100644
--- a/build/reports/problems/problems-report.html
+++ b/build/reports/problems/problems-report.html
@@ -650,7 +650,7 @@ code + .copy-button {