Upload via Git Manager GUI
This commit is contained in:
626
statuspulse.php
626
statuspulse.php
@@ -3,7 +3,7 @@
|
|||||||
Plugin Name: StatusPulse
|
Plugin Name: StatusPulse
|
||||||
Plugin URI:https://git.viper.ipv64.net/M_Viper/StatusPulse
|
Plugin URI:https://git.viper.ipv64.net/M_Viper/StatusPulse
|
||||||
Description: Moderne WordPress-Admin-Seite für StatusAPI-Konfiguration, Attack-API-Key und Proxy-Tests.
|
Description: Moderne WordPress-Admin-Seite für StatusAPI-Konfiguration, Attack-API-Key und Proxy-Tests.
|
||||||
Version: 1.0.0
|
Version: 1.0.1
|
||||||
Author: M_Viper
|
Author: M_Viper
|
||||||
Author URI: https://m-viper.de
|
Author URI: https://m-viper.de
|
||||||
Requires at least: 6.8
|
Requires at least: 6.8
|
||||||
@@ -290,7 +290,118 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return untrailingslashit(esc_url_raw($url));
|
$url = untrailingslashit(esc_url_raw($url));
|
||||||
|
|
||||||
|
// Falls aus anderen Plugins ein Endpoint eingetragen wurde,
|
||||||
|
// auf reine Basis-URL zurückführen.
|
||||||
|
$known_suffixes = array(
|
||||||
|
'/broadcast',
|
||||||
|
'/network/attack',
|
||||||
|
'/antibot/security-log',
|
||||||
|
'/health',
|
||||||
|
);
|
||||||
|
foreach ($known_suffixes as $suffix) {
|
||||||
|
if (substr($url, -strlen($suffix)) === $suffix) {
|
||||||
|
$url = substr($url, 0, -strlen($suffix));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return untrailingslashit($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function get_pulsecast_api_url() {
|
||||||
|
$settings = get_option('pulsecast_settings', array());
|
||||||
|
if (!is_array($settings)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = isset($settings['api_protocol']) ? strtolower(trim((string) $settings['api_protocol'])) : 'http';
|
||||||
|
if ($protocol !== 'http' && $protocol !== 'https') {
|
||||||
|
$protocol = 'http';
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = isset($settings['api_host']) ? trim((string) $settings['api_host']) : '';
|
||||||
|
if ($host === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$port = isset($settings['api_port']) ? trim((string) $settings['api_port']) : '';
|
||||||
|
$path = isset($settings['api_path']) ? trim((string) $settings['api_path']) : '/broadcast';
|
||||||
|
if ($path === '') {
|
||||||
|
$path = '/broadcast';
|
||||||
|
}
|
||||||
|
if (substr($path, 0, 1) !== '/') {
|
||||||
|
$path = '/' . $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $protocol . '://' . $host;
|
||||||
|
if ($port !== '' && !(($protocol === 'http' && $port === '80') || ($protocol === 'https' && $port === '443'))) {
|
||||||
|
$url .= ':' . $port;
|
||||||
|
}
|
||||||
|
// Wichtig: exakt wie PulseCast verwenden (inkl. konfiguriertem Pfad).
|
||||||
|
// So verhalten wir uns identisch zum funktionierenden Plugin.
|
||||||
|
$url .= $path;
|
||||||
|
return esc_url_raw($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function get_candidate_base_urls($base_url) {
|
||||||
|
$candidates = array();
|
||||||
|
$add = function($url) use (&$candidates) {
|
||||||
|
$u = trim((string) $url);
|
||||||
|
if ($u !== '' && !in_array($u, $candidates, true)) {
|
||||||
|
$candidates[] = $u;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$add_base_variants = function($url) use ($add) {
|
||||||
|
$u = trim((string) $url);
|
||||||
|
if ($u === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn nur Domain ohne Schema gespeichert wurde: wie in den anderen Plugins auf http normalisieren.
|
||||||
|
if (strpos($u, 'http://') !== 0 && strpos($u, 'https://') !== 0) {
|
||||||
|
$u = 'http://' . $u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primär: exakt die konfigurierte URL testen.
|
||||||
|
$root = untrailingslashit($u);
|
||||||
|
$add($root);
|
||||||
|
|
||||||
|
// Direkt danach Port 9191 testen – falls NPM nicht korrekt routet, finden
|
||||||
|
// wir den StatusAPI-Server so viel schneller (vor /health etc.)
|
||||||
|
$parts = wp_parse_url($u);
|
||||||
|
if (is_array($parts) && !empty($parts['host']) && empty($parts['port'])) {
|
||||||
|
$host_9191 = 'http://' . $parts['host'] . ':9191';
|
||||||
|
$add($host_9191);
|
||||||
|
$add($host_9191 . '/');
|
||||||
|
$add($host_9191 . '/health');
|
||||||
|
}
|
||||||
|
|
||||||
|
$add($root . '/');
|
||||||
|
$add($root . '/health');
|
||||||
|
|
||||||
|
// Falls https gesetzt ist, auch http testen (häufiger NPM/SSL Sonderfall).
|
||||||
|
if (strpos($root, 'https://') === 0) {
|
||||||
|
$http_root = 'http://' . substr($root, 8);
|
||||||
|
$add($http_root);
|
||||||
|
$add($http_root . '/');
|
||||||
|
$add($http_root . '/health');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$primary = trim((string) $base_url);
|
||||||
|
if ($primary !== '') {
|
||||||
|
$add_base_variants($primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pulsecast_url = self::get_pulsecast_api_url();
|
||||||
|
if ($pulsecast_url !== '') {
|
||||||
|
$add_base_variants($pulsecast_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function get_options() {
|
private static function get_options() {
|
||||||
@@ -404,13 +515,22 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
$live_status = self::probe_statusapi($statusapi_base_url);
|
$live_status = self::probe_statusapi($statusapi_base_url);
|
||||||
$state = self::get_state();
|
$state = self::get_state();
|
||||||
$notice = self::build_notice();
|
$notice = self::build_notice();
|
||||||
$status_endpoint = $statusapi_base_url !== '' ? $statusapi_base_url : 'http://dein-proxy:9191';
|
$candidate_urls_for_ui = self::get_candidate_base_urls($statusapi_base_url);
|
||||||
|
$status_endpoint = !empty($candidate_urls_for_ui)
|
||||||
|
? $candidate_urls_for_ui[0]
|
||||||
|
: '';
|
||||||
$attack_endpoint = $status_endpoint . '/network/attack';
|
$attack_endpoint = $status_endpoint . '/network/attack';
|
||||||
$status_json_snippet = "Status JSON: {$status_endpoint}";
|
$status_json_snippet = "Status JSON: {$status_endpoint}";
|
||||||
$network_guard_snippet = "networkinfo.attack.api_key={$attack_api_key}\nnetworkinfo.attack.source={$attack_source}";
|
$network_guard_snippet = "networkinfo.attack.api_key={$attack_api_key}\nnetworkinfo.attack.source={$attack_source}";
|
||||||
$curl_examples = self::build_curl_examples($status_endpoint, $attack_endpoint, $attack_api_key, $attack_source);
|
$curl_examples = self::build_curl_examples($status_endpoint, $attack_endpoint, $attack_api_key, $attack_source);
|
||||||
$refresh_nonce = wp_create_nonce('statusapi_backend_helper_refresh');
|
$refresh_nonce = wp_create_nonce('statusapi_backend_helper_refresh');
|
||||||
|
|
||||||
|
$debug_data = isset($live_status['debug']) && is_array($live_status['debug']) ? $live_status['debug'] : array();
|
||||||
|
$debug_raw_data = isset($debug_data['raw_data']) ? (string) $debug_data['raw_data'] : '';
|
||||||
|
$debug_error = isset($debug_data['error']) ? (string) $debug_data['error'] : '';
|
||||||
|
$debug_url_used = isset($debug_data['url_used']) ? (string) $debug_data['url_used'] : '';
|
||||||
|
$debug_attempts = isset($debug_data['attempts']) && is_array($debug_data['attempts']) ? $debug_data['attempts'] : array();
|
||||||
|
|
||||||
echo '<div class="wrap statusapi-admin">';
|
echo '<div class="wrap statusapi-admin">';
|
||||||
self::render_styles();
|
self::render_styles();
|
||||||
self::render_scripts($refresh_nonce);
|
self::render_scripts($refresh_nonce);
|
||||||
@@ -473,13 +593,37 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
self::render_metric_card('statusapi-metric-memory', 'RAM Nutzung', $live_status['metrics']['memory'], $live_status['metrics']['memory_meta']);
|
self::render_metric_card('statusapi-metric-memory', 'RAM Nutzung', $live_status['metrics']['memory'], $live_status['metrics']['memory_meta']);
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
|
|
||||||
|
echo '<div class="statusapi-card" style="background: #f9f9f9; border: 2px solid #cbd5e1; margin: 20px 0;">';
|
||||||
|
echo '<div class="statusapi-card-head" style="cursor:pointer;user-select:none;" onclick="var d=document.getElementById(\'statuspulse-debug-body\');d.style.display=(d.style.display===\'none\'?\'block\':\'none\')">';
|
||||||
|
echo '<h2 style="color:#64748b;font-size:16px;">🔍 DEBUG: API Response <span style="font-size:12px;font-weight:normal;">(klicken zum Ein-/Ausblenden)</span></h2>';
|
||||||
|
echo '</div>';
|
||||||
|
echo '<div id="statuspulse-debug-body" style="display:none;">';
|
||||||
|
echo '<div style="padding: 0 15px 10px 15px; font-size: 12px; color: #333;">';
|
||||||
|
echo '<div><strong>URL:</strong> <span id="statuspulse-debug-url">' . esc_html($debug_url_used !== '' ? $debug_url_used : $statusapi_base_url) . '</span></div>';
|
||||||
|
if (!empty($debug_attempts)) {
|
||||||
|
echo '<div><strong>Versuche:</strong> <span id="statuspulse-debug-attempts">' . esc_html(implode(' | ', $debug_attempts)) . '</span></div>';
|
||||||
|
} else {
|
||||||
|
echo '<div><strong>Versuche:</strong> <span id="statuspulse-debug-attempts"></span></div>';
|
||||||
|
}
|
||||||
|
if ($debug_error !== '') {
|
||||||
|
echo '<div id="statuspulse-debug-error" style="color:#c0392b;font-weight:600;margin-top:4px;"><strong>⚠ Fehler:</strong> ' . esc_html($debug_error) . '</div>';
|
||||||
|
} else {
|
||||||
|
echo '<div id="statuspulse-debug-error" style="color:#c0392b;font-weight:600;margin-top:4px;display:none;"></div>';
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
echo '<pre id="statuspulse-debug-raw" style="background: #222; color: #0f0; padding: 15px; border-radius: 4px; overflow-x: auto; font-size: 12px; max-height: 400px; border: 1px solid #444;">';
|
||||||
|
echo esc_html($debug_raw_data !== '' ? $debug_raw_data : '(keine Response)');
|
||||||
|
echo '</pre>';
|
||||||
|
echo '</div>';
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
echo '<div class="statusapi-grid">';
|
echo '<div class="statusapi-grid">';
|
||||||
echo '<div class="statusapi-card statusapi-card-form">';
|
echo '<div class="statusapi-card statusapi-card-form">';
|
||||||
echo '<div class="statusapi-card-head"><h2>Konfiguration</h2><p>Die Werte werden lokal in WordPress gespeichert und als Snippets für deine Proxy-Dateien angezeigt.</p></div>';
|
echo '<div class="statusapi-card-head"><h2>Konfiguration</h2><p>Die Werte werden lokal in WordPress gespeichert und als Snippets für deine Proxy-Dateien angezeigt.</p></div>';
|
||||||
echo '<form method="post" action="options.php">';
|
echo '<form method="post" action="options.php">';
|
||||||
settings_fields('statusapi_backend_helper_group');
|
settings_fields('statusapi_backend_helper_group');
|
||||||
echo '<div class="statusapi-fields">';
|
echo '<div class="statusapi-fields">';
|
||||||
self::render_input_field('StatusAPI Basis-URL', self::OPTION_KEY . '[statusapi_base_url]', $statusapi_base_url, 'z. B. http://127.0.0.1:9191');
|
self::render_input_field('StatusAPI Basis-URL', self::OPTION_KEY . '[statusapi_base_url]', $statusapi_base_url, 'Bei NPM: https://deine-domain.tld (ohne :9191, ohne /broadcast)');
|
||||||
self::render_input_field('Attack API-Key', self::OPTION_KEY . '[attack_api_key]', $attack_api_key, 'Entspricht networkinfo.attack.api_key in network-guard.properties.');
|
self::render_input_field('Attack API-Key', self::OPTION_KEY . '[attack_api_key]', $attack_api_key, 'Entspricht networkinfo.attack.api_key in network-guard.properties.');
|
||||||
self::render_input_field('Attack Source', self::OPTION_KEY . '[attack_source]', $attack_source, 'Wird in Testmeldungen als Quelle angezeigt.');
|
self::render_input_field('Attack Source', self::OPTION_KEY . '[attack_source]', $attack_source, 'Wird in Testmeldungen als Quelle angezeigt.');
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
@@ -513,6 +657,7 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
echo '</div>';
|
echo '</div>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
|
echo '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function render_messages_page() {
|
public static function render_messages_page() {
|
||||||
@@ -690,6 +835,160 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
return "GET {$status_endpoint}\n\nPOST {$attack_endpoint}\nHeader: X-API-Key: {$attack_api_key}\nBody: {$payload}";
|
return "GET {$status_endpoint}\n\nPOST {$attack_endpoint}\nHeader: X-API-Key: {$attack_api_key}\nBody: {$payload}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function is_supported_api_payload($decoded) {
|
||||||
|
if (!is_array($decoded)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_statusapi = isset($decoded['network']) && is_array($decoded['network']);
|
||||||
|
$has_players_array = isset($decoded['players']) && is_array($decoded['players']);
|
||||||
|
$has_online_flag = isset($decoded['online'])
|
||||||
|
&& (is_bool($decoded['online']) || $decoded['online'] === 0 || $decoded['online'] === 1 || $decoded['online'] === '0' || $decoded['online'] === '1' || $decoded['online'] === 'true' || $decoded['online'] === 'false');
|
||||||
|
$is_bungeecord = $has_online_flag && isset($decoded['players']) && is_array($decoded['players']);
|
||||||
|
|
||||||
|
// Wie in mc-player-history: players[] gilt bereits als verwertbares StatusAPI-JSON.
|
||||||
|
return $is_statusapi || $is_bungeecord || $has_players_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function should_try_stream_fallback($error_message) {
|
||||||
|
$msg = strtolower((string) $error_message);
|
||||||
|
return strpos($msg, 'curl error 35') !== false
|
||||||
|
|| strpos($msg, 'tlsv1 unrecognized name') !== false
|
||||||
|
|| strpos($msg, 'ssl routines') !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function stream_http_request($url, $method, $headers, $body, $timeout) {
|
||||||
|
$parts = wp_parse_url($url);
|
||||||
|
if (!is_array($parts) || empty($parts['host'])) {
|
||||||
|
return new WP_Error('statuspulse_stream_bad_url', 'Ungueltige URL fuer Stream-Fallback.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$scheme = isset($parts['scheme']) ? strtolower((string) $parts['scheme']) : 'http';
|
||||||
|
$host = (string) $parts['host'];
|
||||||
|
$port = isset($parts['port']) ? (int) $parts['port'] : ($scheme === 'https' ? 443 : 80);
|
||||||
|
$path = isset($parts['path']) ? (string) $parts['path'] : '/';
|
||||||
|
if ($path === '') {
|
||||||
|
$path = '/';
|
||||||
|
}
|
||||||
|
if (!empty($parts['query'])) {
|
||||||
|
$path .= '?' . $parts['query'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$transport = $scheme === 'https' ? 'ssl://' : 'tcp://';
|
||||||
|
$context_options = array();
|
||||||
|
if ($scheme === 'https') {
|
||||||
|
$context_options['ssl'] = array(
|
||||||
|
'verify_peer' => false,
|
||||||
|
'verify_peer_name' => false,
|
||||||
|
'allow_self_signed' => true,
|
||||||
|
// Workaround fuer Umgebungen mit problematischem SNI/TLS Handshake via cURL.
|
||||||
|
'SNI_enabled' => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$context = stream_context_create($context_options);
|
||||||
|
|
||||||
|
$errno = 0;
|
||||||
|
$errstr = '';
|
||||||
|
$fp = @stream_socket_client($transport . $host . ':' . $port, $errno, $errstr, (float) $timeout, STREAM_CLIENT_CONNECT, $context);
|
||||||
|
if (!$fp) {
|
||||||
|
return new WP_Error('statuspulse_stream_connect_failed', 'Stream-Verbindung fehlgeschlagen: ' . $errstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_set_timeout($fp, (int) max(1, (int) $timeout));
|
||||||
|
|
||||||
|
$request_headers = array(
|
||||||
|
'Host' => $host,
|
||||||
|
'Connection' => 'close',
|
||||||
|
'User-Agent' => 'StatusPulse/1.0',
|
||||||
|
'Accept' => 'application/json, */*',
|
||||||
|
);
|
||||||
|
foreach ((array) $headers as $hk => $hv) {
|
||||||
|
$request_headers[(string) $hk] = (string) $hv;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = (string) $body;
|
||||||
|
if ($payload !== '') {
|
||||||
|
$request_headers['Content-Length'] = (string) strlen($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
$raw = strtoupper($method) . ' ' . $path . " HTTP/1.1\r\n";
|
||||||
|
foreach ($request_headers as $hk => $hv) {
|
||||||
|
$raw .= $hk . ': ' . $hv . "\r\n";
|
||||||
|
}
|
||||||
|
$raw .= "\r\n";
|
||||||
|
if ($payload !== '') {
|
||||||
|
$raw .= $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite($fp, $raw);
|
||||||
|
|
||||||
|
$response_raw = '';
|
||||||
|
while (!feof($fp)) {
|
||||||
|
$chunk = fread($fp, 8192);
|
||||||
|
if ($chunk === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$response_raw .= $chunk;
|
||||||
|
}
|
||||||
|
fclose($fp);
|
||||||
|
|
||||||
|
if ($response_raw === '') {
|
||||||
|
return new WP_Error('statuspulse_stream_empty', 'Leere Antwort vom Stream-Fallback.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts_resp = explode("\r\n\r\n", $response_raw, 2);
|
||||||
|
$head = isset($parts_resp[0]) ? $parts_resp[0] : '';
|
||||||
|
$resp_body = isset($parts_resp[1]) ? $parts_resp[1] : '';
|
||||||
|
|
||||||
|
$status_code = 0;
|
||||||
|
$head_lines = explode("\r\n", $head);
|
||||||
|
if (!empty($head_lines)) {
|
||||||
|
if (preg_match('#HTTP/\d\.\d\s+(\d{3})#', (string) $head_lines[0], $m)) {
|
||||||
|
$status_code = (int) $m[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'code' => $status_code,
|
||||||
|
'body' => $resp_body,
|
||||||
|
'error' => '',
|
||||||
|
'via' => 'stream',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function perform_request_with_fallback($url, $method, $headers = array(), $body = '', $timeout = 5) {
|
||||||
|
$args = array(
|
||||||
|
'timeout' => $timeout,
|
||||||
|
'sslverify' => false,
|
||||||
|
'headers' => is_array($headers) ? $headers : array(),
|
||||||
|
);
|
||||||
|
if (strtoupper($method) !== 'GET') {
|
||||||
|
$args['body'] = (string) $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = wp_remote_request($url, array_merge($args, array('method' => strtoupper($method))));
|
||||||
|
if (!is_wp_error($response)) {
|
||||||
|
return array(
|
||||||
|
'code' => (int) wp_remote_retrieve_response_code($response),
|
||||||
|
'body' => (string) wp_remote_retrieve_body($response),
|
||||||
|
'error' => '',
|
||||||
|
'via' => 'wp',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$error_message = $response->get_error_message();
|
||||||
|
if (!self::should_try_stream_fallback($error_message)) {
|
||||||
|
return array('code' => 0, 'body' => '', 'error' => $error_message, 'via' => 'wp');
|
||||||
|
}
|
||||||
|
|
||||||
|
$fallback = self::stream_http_request($url, $method, $headers, $body, $timeout);
|
||||||
|
if (is_wp_error($fallback)) {
|
||||||
|
return array('code' => 0, 'body' => '', 'error' => $error_message . ' | Stream-Fallback: ' . $fallback->get_error_message(), 'via' => 'wp+stream');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fallback;
|
||||||
|
}
|
||||||
|
|
||||||
private static function probe_statusapi($base_url) {
|
private static function probe_statusapi($base_url) {
|
||||||
$state = self::get_state();
|
$state = self::get_state();
|
||||||
$previous_status = isset($state['last_status']) ? (string) $state['last_status'] : 'unknown';
|
$previous_status = isset($state['last_status']) ? (string) $state['last_status'] : 'unknown';
|
||||||
@@ -697,43 +996,113 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
$previous_memory_high = !empty($state['last_memory_high']);
|
$previous_memory_high = !empty($state['last_memory_high']);
|
||||||
$previous_player_high = !empty($state['last_player_high']);
|
$previous_player_high = !empty($state['last_player_high']);
|
||||||
|
|
||||||
if ($base_url === '') {
|
$candidate_urls = self::get_candidate_base_urls($base_url);
|
||||||
|
if (empty($candidate_urls)) {
|
||||||
return array(
|
return array(
|
||||||
'status' => 'unknown',
|
'status' => 'unknown',
|
||||||
'label' => 'Nicht konfiguriert',
|
'label' => 'Nicht konfiguriert',
|
||||||
'meta' => 'Bitte zuerst eine StatusAPI Basis-URL eintragen.',
|
'meta' => 'Bitte zuerst eine StatusAPI Basis-URL eintragen (oder PulseCast konfigurieren).',
|
||||||
'http_code' => 0,
|
'http_code' => 0,
|
||||||
'checked_at_human' => self::format_timestamp($state['last_check_at']),
|
'checked_at_human' => self::format_timestamp($state['last_check_at']),
|
||||||
'metrics' => self::empty_metrics(),
|
'metrics' => self::empty_metrics(),
|
||||||
|
'debug' => array(
|
||||||
|
'url_used' => '',
|
||||||
|
'attempts' => array(),
|
||||||
|
'error' => 'Keine Basis-URL gesetzt.',
|
||||||
|
'raw_data' => '',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = wp_remote_get($base_url, array('timeout' => 5));
|
$response_url = $candidate_urls[0];
|
||||||
|
$response = null;
|
||||||
|
$last_error = '';
|
||||||
|
$is_valid_response = false;
|
||||||
|
$is_reachable = false;
|
||||||
|
$reachable_code = 0;
|
||||||
|
$attempts = array();
|
||||||
|
$raw_fallback = '';
|
||||||
|
|
||||||
|
foreach ($candidate_urls as $try_url) {
|
||||||
|
$response = self::perform_request_with_fallback($try_url, 'GET', array(), '', 5);
|
||||||
|
|
||||||
|
if (!empty($response['error'])) {
|
||||||
|
$last_error = $response['error'];
|
||||||
|
$attempts[] = $try_url . ' -> ERROR: ' . $response['error'];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$code_tmp = (int) $response['code'];
|
||||||
|
$body_tmp = (string) $response['body'];
|
||||||
|
$decoded_tmp = json_decode($body_tmp, true);
|
||||||
|
|
||||||
|
if ($code_tmp > 0 && $code_tmp < 500 && !$is_reachable) {
|
||||||
|
$is_reachable = true;
|
||||||
|
$reachable_code = $code_tmp;
|
||||||
|
$response_url = $try_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code_tmp >= 200 && $code_tmp < 300 && self::is_supported_api_payload($decoded_tmp)) {
|
||||||
|
$response_url = $try_url;
|
||||||
|
$is_valid_response = true;
|
||||||
|
$last_error = '';
|
||||||
|
$attempts[] = $try_url . ' -> HTTP ' . $code_tmp . ' (StatusAPI JSON)';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code_tmp >= 200 && $code_tmp < 300 && !self::is_supported_api_payload($decoded_tmp)) {
|
||||||
|
$attempts[] = $try_url . ' -> HTTP ' . $code_tmp . ' (kein StatusAPI JSON)';
|
||||||
|
if ($raw_fallback === '') {
|
||||||
|
$raw_fallback = $body_tmp;
|
||||||
|
}
|
||||||
|
if (is_string($body_tmp) && stripos($body_tmp, 'successfully started the Nginx Proxy Manager') !== false) {
|
||||||
|
// NPM-Domain ist nicht korrekt auf Port 9191 geroutet
|
||||||
|
$parsed_npm = wp_parse_url($try_url);
|
||||||
|
$npm_host = is_array($parsed_npm) && !empty($parsed_npm['host']) ? $parsed_npm['host'] : '';
|
||||||
|
$last_error = 'Nginx Proxy Manager Default-Seite erkannt – Proxy-Host für diese Domain fehlt.'
|
||||||
|
. ($npm_host !== '' ? ' Trage stattdessen direkt ein: http://' . $npm_host . ':9191' : '');
|
||||||
|
} else {
|
||||||
|
$last_error = 'Antwort ist kein StatusAPI-JSON.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$attempts[] = $try_url . ' -> HTTP ' . $code_tmp;
|
||||||
|
$last_error = 'HTTP ' . $code_tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$now = time();
|
$now = time();
|
||||||
|
|
||||||
if (is_wp_error($response)) {
|
if (!$is_valid_response) {
|
||||||
$state['last_check_at'] = $now;
|
$state['last_check_at'] = $now;
|
||||||
$state['last_http_code'] = 0;
|
$state['last_http_code'] = $is_reachable ? $reachable_code : 0;
|
||||||
$state['last_status'] = 'error';
|
$state['last_status'] = 'error';
|
||||||
$state['last_error'] = $response->get_error_message();
|
$state['last_error'] = $last_error !== '' ? $last_error : 'Ungueltige Antwort vom Proxy';
|
||||||
if ($previous_status !== 'error') {
|
if ($previous_status !== 'error') {
|
||||||
self::push_message($state, 'error', 'Proxy offline', 'StatusAPI ist nicht erreichbar: ' . $response->get_error_message());
|
self::push_message($state, 'error', 'Proxy-Fehlkonfiguration', 'StatusAPI liefert keine gueltigen JSON-Daten. Pruefe Nginx Proxy Host fuer die Domain.');
|
||||||
}
|
}
|
||||||
self::save_state($state);
|
self::save_state($state);
|
||||||
|
|
||||||
|
error_log('StatusPulse: Fehler fuer ' . implode(', ', $candidate_urls) . ': ' . $state['last_error']);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'label' => 'Offline',
|
'label' => 'Fehlkonfiguration',
|
||||||
'meta' => $response->get_error_message(),
|
'meta' => $state['last_error'],
|
||||||
'http_code' => 0,
|
'http_code' => 0,
|
||||||
'checked_at_human' => self::format_timestamp($now),
|
'checked_at_human' => self::format_timestamp($now),
|
||||||
'metrics' => self::empty_metrics(),
|
'metrics' => self::empty_metrics(),
|
||||||
|
'debug' => array(
|
||||||
|
'url_used' => $response_url,
|
||||||
|
'attempts' => $attempts,
|
||||||
|
'error' => $state['last_error'],
|
||||||
|
'raw_data' => $raw_fallback,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$code = (int) wp_remote_retrieve_response_code($response);
|
$code = (int) $response['code'];
|
||||||
$ok = $code >= 200 && $code < 300;
|
$ok = $code >= 200 && $code < 300;
|
||||||
$body = wp_remote_retrieve_body($response);
|
$body = (string) $response['body'];
|
||||||
$decoded = json_decode($body, true);
|
$decoded = json_decode($body, true);
|
||||||
$state['last_check_at'] = $now;
|
$state['last_check_at'] = $now;
|
||||||
$state['last_http_code'] = $code;
|
$state['last_http_code'] = $code;
|
||||||
@@ -800,10 +1169,16 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
return array(
|
return array(
|
||||||
'status' => $ok ? 'ok' : 'error',
|
'status' => $ok ? 'ok' : 'error',
|
||||||
'label' => $ok ? 'Online' : 'Fehler',
|
'label' => $ok ? 'Online' : 'Fehler',
|
||||||
'meta' => $ok ? 'Antwort von ' . untrailingslashit($base_url) : 'HTTP ' . $code,
|
'meta' => $ok ? 'Antwort von ' . untrailingslashit($response_url) : 'HTTP ' . $code,
|
||||||
'http_code' => $code,
|
'http_code' => $code,
|
||||||
'checked_at_human' => self::format_timestamp($now),
|
'checked_at_human' => self::format_timestamp($now),
|
||||||
'metrics' => $ok ? self::extract_metrics($decoded) : self::empty_metrics(),
|
'metrics' => $ok ? self::extract_metrics($decoded) : self::empty_metrics(),
|
||||||
|
'debug' => array(
|
||||||
|
'url_used' => $response_url,
|
||||||
|
'attempts' => $attempts,
|
||||||
|
'error' => $ok ? '' : 'HTTP ' . $code,
|
||||||
|
'raw_data' => $body,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,19 +1187,33 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
return self::empty_metrics();
|
return self::empty_metrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$players_list_count = self::count_players_fallback($decoded);
|
||||||
|
|
||||||
|
// StatusAPI-Format
|
||||||
$network = isset($decoded['network']) && is_array($decoded['network']) ? $decoded['network'] : array();
|
$network = isset($decoded['network']) && is_array($decoded['network']) ? $decoded['network'] : array();
|
||||||
|
if (!empty($network)) {
|
||||||
$players = isset($network['players']) && is_array($network['players']) ? $network['players'] : array();
|
$players = isset($network['players']) && is_array($network['players']) ? $network['players'] : array();
|
||||||
$memory = isset($network['memory']) && is_array($network['memory']) ? $network['memory'] : array();
|
$memory = isset($network['memory']) && is_array($network['memory']) ? $network['memory'] : array();
|
||||||
|
|
||||||
$online_players = self::array_value($players, 'online', self::count_players_fallback($decoded));
|
// Wie in MC-Player-History: players[] als primäre Quelle bevorzugen.
|
||||||
$max_players = self::array_value($players, 'max', self::array_value($decoded, 'max_players', 'n/a'));
|
$online_players = $players_list_count !== 'n/a'
|
||||||
|
? $players_list_count
|
||||||
|
: self::array_value($players, 'online', 'n/a');
|
||||||
|
|
||||||
|
$max_players_raw = self::array_value($players, 'max', self::array_value($decoded, 'max_players', 'n/a'));
|
||||||
|
$max_players = self::normalize_max_players($max_players_raw);
|
||||||
$occupancy = self::array_value($players, 'occupancy_percent', null);
|
$occupancy = self::array_value($players, 'occupancy_percent', null);
|
||||||
$uptime_human = self::array_value($network, 'uptime_human', 'n/a');
|
$uptime_human = self::array_value($network, 'uptime_human', 'n/a');
|
||||||
$uptime_seconds = self::array_value($network, 'uptime_seconds', null);
|
$uptime_seconds = self::array_value($network, 'uptime_seconds', null);
|
||||||
$memory_percent = self::array_value($memory, 'usage_percent', 'n/a');
|
$memory_percent = self::array_value($memory, 'usage_percent', null);
|
||||||
$memory_used = self::array_value($memory, 'used_mb', 'n/a');
|
$memory_used = self::array_value($memory, 'used_mb', 'n/a');
|
||||||
$memory_max = self::array_value($memory, 'max_mb', 'n/a');
|
$memory_max = self::array_value($memory, 'max_mb', 'n/a');
|
||||||
|
|
||||||
|
$memory_value = 'n/a';
|
||||||
|
if ($memory_percent !== null && $memory_percent !== 'n/a' && $memory_percent !== '') {
|
||||||
|
$memory_value = ((string) $memory_percent) . '%';
|
||||||
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'online_players' => (string) $online_players,
|
'online_players' => (string) $online_players,
|
||||||
'online_meta' => $occupancy !== null ? 'Auslastung ' . $occupancy . '%' : 'Aktuell verbundene Spieler',
|
'online_meta' => $occupancy !== null ? 'Auslastung ' . $occupancy . '%' : 'Aktuell verbundene Spieler',
|
||||||
@@ -832,11 +1221,48 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
'max_meta' => 'Player-Limit laut Proxy',
|
'max_meta' => 'Player-Limit laut Proxy',
|
||||||
'uptime' => (string) $uptime_human,
|
'uptime' => (string) $uptime_human,
|
||||||
'uptime_meta' => $uptime_seconds !== null ? ((string) $uptime_seconds) . ' Sekunden' : 'Keine Uptime-Daten',
|
'uptime_meta' => $uptime_seconds !== null ? ((string) $uptime_seconds) . ' Sekunden' : 'Keine Uptime-Daten',
|
||||||
'memory' => ((string) $memory_percent) . '%',
|
'memory' => $memory_value,
|
||||||
'memory_meta' => $memory_used . ' MB von ' . $memory_max . ' MB',
|
'memory_meta' => $memory_used . ' MB von ' . $memory_max . ' MB',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BungeeCord-Format
|
||||||
|
$is_bungeecord = isset($decoded['players']) && is_array($decoded['players']) && isset($decoded['online']);
|
||||||
|
if ($is_bungeecord) {
|
||||||
|
return self::extract_metrics_bungeecord($decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::empty_metrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function extract_metrics_bungeecord($decoded) {
|
||||||
|
if (!is_array($decoded)) {
|
||||||
|
return self::empty_metrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
$players_array = isset($decoded['players']) && is_array($decoded['players']) ? $decoded['players'] : array();
|
||||||
|
$online_raw = self::array_value($decoded, 'online', null);
|
||||||
|
$is_online = $online_raw === true || $online_raw === 1 || $online_raw === '1' || $online_raw === 'true';
|
||||||
|
$online_count = $is_online ? count($players_array) : '0';
|
||||||
|
|
||||||
|
$max_players = self::array_value($decoded, 'max_players', 'n/a');
|
||||||
|
if ($max_players === 'n/a' && !empty($players_array) && is_array($players_array[0]) && isset($players_array[0]['max'])) {
|
||||||
|
$max_players = (int) $players_array[0]['max'];
|
||||||
|
}
|
||||||
|
$max_players = self::normalize_max_players($max_players);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'online_players' => (string) $online_count,
|
||||||
|
'online_meta' => 'Spieler online (BungeeCord)',
|
||||||
|
'max_players' => (string) $max_players,
|
||||||
|
'max_meta' => 'Max Spieler',
|
||||||
|
'uptime' => 'n/a',
|
||||||
|
'uptime_meta' => 'BungeeCord Format (nicht verfügbar)',
|
||||||
|
'memory' => 'n/a',
|
||||||
|
'memory_meta' => 'BungeeCord Format (nicht verfügbar)',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private static function empty_metrics() {
|
private static function empty_metrics() {
|
||||||
return array(
|
return array(
|
||||||
'online_players' => 'n/a',
|
'online_players' => 'n/a',
|
||||||
@@ -864,6 +1290,20 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
return 'n/a';
|
return 'n/a';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function normalize_max_players($value) {
|
||||||
|
// -1 bedeutet: BungeeCord hat keinen Listener-Wert und kein globales Limit.
|
||||||
|
// Wird ab StatusAPI 4.1+ eigentlich nicht mehr auftreten da Java den Listener-Wert liefert.
|
||||||
|
if ($value === -1 || $value === '-1' || (is_numeric($value) && (int) $value === -1)) {
|
||||||
|
return '∞';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_numeric($value) && (int) $value <= 0) {
|
||||||
|
return 'n/a';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
private static function format_timestamp($timestamp) {
|
private static function format_timestamp($timestamp) {
|
||||||
$timestamp = (int) $timestamp;
|
$timestamp = (int) $timestamp;
|
||||||
if ($timestamp <= 0) {
|
if ($timestamp <= 0) {
|
||||||
@@ -892,7 +1332,12 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
return array('type' => 'error', 'message' => 'Bitte zuerst einen Attack API-Key speichern.');
|
return array('type' => 'error', 'message' => 'Bitte zuerst einen Attack API-Key speichern.');
|
||||||
}
|
}
|
||||||
if ($code === 'connection_failed') {
|
if ($code === 'connection_failed') {
|
||||||
return array('type' => 'error', 'message' => 'Verbindungstest fehlgeschlagen. Bitte Basis-URL und Erreichbarkeit prüfen.');
|
$state = self::get_state();
|
||||||
|
$suffix = '';
|
||||||
|
if (!empty($state['last_error'])) {
|
||||||
|
$suffix = ' Letzter Fehler: ' . (string) $state['last_error'];
|
||||||
|
}
|
||||||
|
return array('type' => 'error', 'message' => 'Verbindungstest fehlgeschlagen. Bitte Basis-URL und Erreichbarkeit prüfen.' . $suffix);
|
||||||
}
|
}
|
||||||
if ($code === 'attack_failed') {
|
if ($code === 'attack_failed') {
|
||||||
return array('type' => 'error', 'message' => 'Test Attack-Meldung konnte nicht gesendet werden. API-Key und Proxy-Endpunkt prüfen.');
|
return array('type' => 'error', 'message' => 'Test Attack-Meldung konnte nicht gesendet werden. API-Key und Proxy-Endpunkt prüfen.');
|
||||||
@@ -918,13 +1363,16 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_POST['test_connection'])) {
|
if (isset($_POST['test_connection'])) {
|
||||||
$ok = self::request_ok($base_url);
|
$probe = self::probe_statusapi($base_url);
|
||||||
|
$ok = isset($probe['status']) && $probe['status'] === 'ok';
|
||||||
$state = self::get_state();
|
$state = self::get_state();
|
||||||
self::push_message(
|
self::push_message(
|
||||||
$state,
|
$state,
|
||||||
$ok ? 'success' : 'error',
|
$ok ? 'success' : 'error',
|
||||||
$ok ? 'Verbindungstest erfolgreich' : 'Verbindungstest fehlgeschlagen',
|
$ok ? 'Verbindungstest erfolgreich' : 'Verbindungstest fehlgeschlagen',
|
||||||
$ok ? 'Manueller Verbindungstest im WordPress-Backend war erfolgreich.' : 'Manueller Verbindungstest im WordPress-Backend war nicht erfolgreich.'
|
$ok
|
||||||
|
? 'Manueller Verbindungstest im WordPress-Backend war erfolgreich.'
|
||||||
|
: 'Manueller Verbindungstest im WordPress-Backend war nicht erfolgreich. ' . (isset($probe['meta']) ? (string) $probe['meta'] : '')
|
||||||
);
|
);
|
||||||
self::save_state($state);
|
self::save_state($state);
|
||||||
self::redirect_with_notice($ok ? 'connection_ok' : 'connection_failed');
|
self::redirect_with_notice($ok ? 'connection_ok' : 'connection_failed');
|
||||||
@@ -943,27 +1391,29 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
'connectionsBlocked' => 1800,
|
'connectionsBlocked' => 1800,
|
||||||
);
|
);
|
||||||
|
|
||||||
$response = wp_remote_post($base_url . '/network/attack', array(
|
$response = self::perform_request_with_fallback(
|
||||||
'timeout' => 8,
|
$base_url . '/network/attack',
|
||||||
'headers' => array(
|
'POST',
|
||||||
|
array(
|
||||||
'Content-Type' => 'application/json; charset=utf-8',
|
'Content-Type' => 'application/json; charset=utf-8',
|
||||||
'X-API-Key' => $options['attack_api_key'],
|
'X-API-Key' => $options['attack_api_key'],
|
||||||
),
|
),
|
||||||
'body' => wp_json_encode($payload),
|
wp_json_encode($payload),
|
||||||
));
|
8
|
||||||
|
);
|
||||||
|
|
||||||
if (is_wp_error($response)) {
|
if (!empty($response['error'])) {
|
||||||
$state = self::get_state();
|
$state = self::get_state();
|
||||||
$state['last_check_at'] = time();
|
$state['last_check_at'] = time();
|
||||||
$state['last_http_code'] = 0;
|
$state['last_http_code'] = 0;
|
||||||
$state['last_status'] = 'error';
|
$state['last_status'] = 'error';
|
||||||
$state['last_error'] = $response->get_error_message();
|
$state['last_error'] = $response['error'];
|
||||||
self::push_message($state, 'error', 'Test-Attack fehlgeschlagen', 'Konnte keine Test-Meldung senden: ' . $response->get_error_message());
|
self::push_message($state, 'error', 'Test-Attack fehlgeschlagen', 'Konnte keine Test-Meldung senden: ' . $response['error']);
|
||||||
self::save_state($state);
|
self::save_state($state);
|
||||||
self::redirect_with_notice('attack_failed');
|
self::redirect_with_notice('attack_failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
$code = (int) wp_remote_retrieve_response_code($response);
|
$code = (int) $response['code'];
|
||||||
$state = self::get_state();
|
$state = self::get_state();
|
||||||
$state['last_check_at'] = time();
|
$state['last_check_at'] = time();
|
||||||
$state['last_http_code'] = $code;
|
$state['last_http_code'] = $code;
|
||||||
@@ -1020,9 +1470,17 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
$live_status = self::probe_statusapi($options['statusapi_base_url']);
|
$live_status = self::probe_statusapi($options['statusapi_base_url']);
|
||||||
$state = self::get_state();
|
$state = self::get_state();
|
||||||
|
|
||||||
|
$debug_data = isset($live_status['debug']) && is_array($live_status['debug']) ? $live_status['debug'] : array();
|
||||||
|
|
||||||
wp_send_json_success(array(
|
wp_send_json_success(array(
|
||||||
'refresh_state' => 'Aktualisiert ' . self::format_timestamp($state['last_check_at']),
|
'refresh_state' => 'Aktualisiert ' . self::format_timestamp($state['last_check_at']),
|
||||||
'messages_html' => self::build_messages_markup($state),
|
'messages_html' => self::build_messages_markup($state),
|
||||||
|
'debug' => array(
|
||||||
|
'url_used' => isset($debug_data['url_used']) ? (string) $debug_data['url_used'] : '',
|
||||||
|
'attempts' => isset($debug_data['attempts']) && is_array($debug_data['attempts']) ? $debug_data['attempts'] : array(),
|
||||||
|
'error' => isset($debug_data['error']) ? (string) $debug_data['error'] : '',
|
||||||
|
'raw_data' => isset($debug_data['raw_data']) ? (string) $debug_data['raw_data'] : '',
|
||||||
|
),
|
||||||
'status_cards' => array(
|
'status_cards' => array(
|
||||||
'proxy' => array(
|
'proxy' => array(
|
||||||
'value' => $live_status['label'],
|
'value' => $live_status['label'],
|
||||||
@@ -1067,52 +1525,108 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static function request_ok($url) {
|
private static function request_ok($url) {
|
||||||
$response = wp_remote_get($url, array('timeout' => 8));
|
$candidate_urls = self::get_candidate_base_urls($url);
|
||||||
if (is_wp_error($response)) {
|
if (empty($candidate_urls)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$code = (int) wp_remote_retrieve_response_code($response);
|
foreach ($candidate_urls as $try_url) {
|
||||||
return $code >= 200 && $code < 300;
|
$response = self::perform_request_with_fallback($try_url, 'GET', array(), '', 8);
|
||||||
|
|
||||||
|
if (!empty($response['error'])) {
|
||||||
|
error_log('StatusPulse: request_ok() fehlgeschlagen fuer ' . $try_url . ' - ' . $response['error']);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = (int) $response['code'];
|
||||||
|
$decoded = json_decode((string) $response['body'], true);
|
||||||
|
error_log('StatusPulse: request_ok() fuer ' . $try_url . ' -> HTTP ' . $code);
|
||||||
|
if ($code >= 200 && $code < 300 && self::is_supported_api_payload($decoded)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function fetch_attacker_entries($base_url) {
|
private static function fetch_attacker_entries($base_url) {
|
||||||
if ($base_url === '') {
|
// Schritt 1: Arbeitsfähige Basis-URL ermitteln (erste die valide antwortet)
|
||||||
|
$candidate_urls = self::get_candidate_base_urls($base_url);
|
||||||
|
if (empty($candidate_urls)) {
|
||||||
return array(
|
return array(
|
||||||
'entries' => array(),
|
'entries' => array(),
|
||||||
'error' => 'StatusAPI Basis-URL ist nicht konfiguriert.',
|
'error' => 'StatusAPI Basis-URL ist nicht konfiguriert.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$endpoint = untrailingslashit($base_url) . '/antibot/security-log';
|
// Finde die funktionierende Basis-URL (die echtes StatusAPI-JSON liefert)
|
||||||
$response = wp_remote_get($endpoint, array('timeout' => 8));
|
$working_base = '';
|
||||||
if (is_wp_error($response)) {
|
foreach ($candidate_urls as $try_url) {
|
||||||
|
// Entferne bekannte Pfad-Suffixe damit wir nur die Root-URL testen
|
||||||
|
$root_url = rtrim(preg_replace('#/(health|antibot/security-log|broadcast|network/attack)$#', '', $try_url), '/');
|
||||||
|
if ($root_url === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$probe = self::perform_request_with_fallback($root_url, 'GET', array(), '', 8);
|
||||||
|
if (empty($probe['error'])) {
|
||||||
|
$probe_code = (int) $probe['code'];
|
||||||
|
$probe_decoded = json_decode((string) $probe['body'], true);
|
||||||
|
if ($probe_code >= 200 && $probe_code < 300 && self::is_supported_api_payload($probe_decoded)) {
|
||||||
|
$working_base = $root_url;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($working_base === '') {
|
||||||
return array(
|
return array(
|
||||||
'entries' => array(),
|
'entries' => array(),
|
||||||
'error' => 'Konnte Angriffsdaten nicht laden: ' . $response->get_error_message(),
|
'error' => 'StatusAPI konnte nicht erreicht werden. Bitte Basis-URL und Erreichbarkeit prüfen.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$code = (int) wp_remote_retrieve_response_code($response);
|
// Schritt 2: Dedizierter Endpunkt /antibot/security-log abrufen
|
||||||
|
$security_log_url = rtrim($working_base, '/') . '/antibot/security-log';
|
||||||
|
$response = self::perform_request_with_fallback($security_log_url, 'GET', array(), '', 8);
|
||||||
|
|
||||||
|
if (!empty($response['error'])) {
|
||||||
|
return array(
|
||||||
|
'entries' => array(),
|
||||||
|
'error' => 'Konnte Security-Log nicht laden: ' . $response['error'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = (int) $response['code'];
|
||||||
if ($code < 200 || $code >= 300) {
|
if ($code < 200 || $code >= 300) {
|
||||||
return array(
|
return array(
|
||||||
'entries' => array(),
|
'entries' => array(),
|
||||||
'error' => 'StatusAPI lieferte HTTP ' . $code . ' für /antibot/security-log.',
|
'error' => 'StatusAPI /antibot/security-log lieferte HTTP ' . $code . '.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$body = wp_remote_retrieve_body($response);
|
$body = (string) $response['body'];
|
||||||
$decoded = json_decode($body, true);
|
$decoded = json_decode($body, true);
|
||||||
if (!is_array($decoded) || !isset($decoded['events']) || !is_array($decoded['events'])) {
|
if (!is_array($decoded)) {
|
||||||
return array(
|
return array(
|
||||||
'entries' => array(),
|
'entries' => array(),
|
||||||
'error' => 'Antwort von /antibot/security-log ist ungültig.',
|
'error' => 'Antwort vom Security-Log-Endpunkt ist kein gültiges JSON.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$entries = array();
|
||||||
|
|
||||||
|
// StatusAPI liefert { "success": true, "events": [...] }
|
||||||
|
if (isset($decoded['events']) && is_array($decoded['events'])) {
|
||||||
|
foreach (array_slice($decoded['events'], 0, 100) as $entry) {
|
||||||
|
if (is_array($entry)) {
|
||||||
|
$entries[] = $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'entries' => $decoded['events'],
|
'entries' => $entries,
|
||||||
'error' => '',
|
'error' => count($entries) === 0 ? 'Keine Angriffsversuche im Log vorhanden.' : '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1699,6 +2213,22 @@ if (!class_exists('StatusAPI_Backend_Helper')) {
|
|||||||
applyMetricCard("statusapi-metric-uptime", result.data.metric_cards.uptime);
|
applyMetricCard("statusapi-metric-uptime", result.data.metric_cards.uptime);
|
||||||
applyMetricCard("statusapi-metric-memory", result.data.metric_cards.memory);
|
applyMetricCard("statusapi-metric-memory", result.data.metric_cards.memory);
|
||||||
|
|
||||||
|
// Debug-Panel live aktualisieren
|
||||||
|
if (result.data.debug) {
|
||||||
|
var dbg = result.data.debug;
|
||||||
|
var urlEl = document.getElementById("statuspulse-debug-url");
|
||||||
|
var attEl = document.getElementById("statuspulse-debug-attempts");
|
||||||
|
var rawEl = document.getElementById("statuspulse-debug-raw");
|
||||||
|
var errEl = document.getElementById("statuspulse-debug-error");
|
||||||
|
if (urlEl) urlEl.textContent = dbg.url_used || "(nicht gesetzt)";
|
||||||
|
if (attEl) attEl.textContent = dbg.attempts && dbg.attempts.length ? dbg.attempts.join(" | ") : "";
|
||||||
|
if (rawEl) rawEl.textContent = dbg.raw_data || "(keine Response)";
|
||||||
|
if (errEl) {
|
||||||
|
errEl.textContent = dbg.error || "";
|
||||||
|
errEl.style.display = dbg.error ? "block" : "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var messageList = document.getElementById("statusapi-message-list");
|
var messageList = document.getElementById("statusapi-message-list");
|
||||||
if (messageList && typeof result.data.messages_html !== "undefined") {
|
if (messageList && typeof result.data.messages_html !== "undefined") {
|
||||||
messageList.innerHTML = result.data.messages_html;
|
messageList.innerHTML = result.data.messages_html;
|
||||||
|
|||||||
Reference in New Issue
Block a user