';
self::render_styles();
self::render_scripts($refresh_nonce);
echo '
';
echo '
';
echo '
Proxy Control Surface';
echo '
StatusPulse
';
echo '
Reduzierte Admin-Seite für Proxy-URL, Attack-Key und direkte Tests gegen dein StatusAPI-Plugin.
';
echo '
';
echo '
';
echo 'Live Refresh';
echo 'alle 15s';
echo 'wartet auf erstes Update';
echo '
';
echo '
';
if ($notice !== null) {
printf(
'
',
esc_attr($notice['type']),
esc_html($notice['message'])
);
}
echo '
';
self::render_status_card_named(
'statusapi-card-proxy',
'Proxy Status',
$live_status['label'],
$live_status['meta'],
$live_status['status']
);
self::render_status_card_named(
'statusapi-card-http',
'Letzter HTTP-Code',
$live_status['http_code'] > 0 ? (string) $live_status['http_code'] : 'n/a',
$live_status['checked_at_human'],
$live_status['status']
);
self::render_status_card_named(
'statusapi-card-success',
'Letzte erfolgreiche Prüfung',
self::format_timestamp($state['last_success_at']),
$state['last_success_code'] > 0 ? 'HTTP ' . $state['last_success_code'] : 'Noch kein erfolgreicher Check gespeichert',
$state['last_success_at'] > 0 ? 'ok' : 'unknown'
);
self::render_status_card_named(
'statusapi-card-lastcheck',
'Zuletzt geprüft',
self::format_timestamp($state['last_check_at']),
$state['last_error'] !== '' ? $state['last_error'] : 'Keine Fehler gespeichert',
$state['last_status']
);
echo '
';
echo '
';
self::render_metric_card('statusapi-metric-online', 'Spieler online', $live_status['metrics']['online_players'], $live_status['metrics']['online_meta']);
self::render_metric_card('statusapi-metric-max', 'Max. Spieler', $live_status['metrics']['max_players'], $live_status['metrics']['max_meta']);
self::render_metric_card('statusapi-metric-uptime', 'Uptime', $live_status['metrics']['uptime'], $live_status['metrics']['uptime_meta']);
self::render_metric_card('statusapi-metric-memory', 'RAM Nutzung', $live_status['metrics']['memory'], $live_status['metrics']['memory_meta']);
echo '
';
echo '
';
echo '
';
echo '
';
echo '
Snippets
Direkt für deine Proxy-Konfiguration und externe Tests vorbereitet.
';
self::render_snippet_card('Für network-guard.properties', $network_guard_snippet, 4);
self::render_snippet_card('Wichtiger Endpunkt', $status_json_snippet . "\nAttack POST: {$attack_endpoint}", 4);
self::render_snippet_card('Test-Requests', $curl_examples, 8);
echo '
';
echo '
';
echo '
Aktionen
Teste direkt aus dem Backend, ob dein Proxy erreichbar ist und Attack-Meldungen akzeptiert.
';
echo '
';
echo '
';
echo '
';
echo '
';
}
public static function render_messages_page() {
if (!current_user_can('manage_options')) {
return;
}
$state = self::get_state();
$notice = self::build_notice();
$messages_markup = self::build_messages_markup($state);
$refresh_nonce = wp_create_nonce('statusapi_backend_helper_refresh');
echo '
';
self::render_styles();
self::render_scripts($refresh_nonce);
echo '
StatusPulse Meldungen
';
echo '
Dedizierte Seite für alle laufenden Status-, Attack- und Warnmeldungen.
';
if ($notice !== null) {
printf(
'
',
esc_attr($notice['type']),
esc_html($notice['message'])
);
}
self::render_messages_center($messages_markup, self::MESSAGES_SLUG);
echo '
';
}
private static function render_messages_center($messages_markup, $redirect_page) {
echo '
';
echo '
Meldungs-Center
Hier siehst du laufend erkannte Status-, Attack- und Warnmeldungen aus deinem Proxy.
';
echo '
';
echo '';
echo '';
echo '';
echo '';
echo '';
echo '';
echo '
';
echo '
';
echo '
';
}
private static function render_input_field($label, $name, $value, $description) {
echo '
';
}
private static function render_snippet_card($title, $content, $rows) {
echo '
';
echo '
';
echo '
' . esc_html($title) . '
';
echo '';
echo '';
printf(
'
',
(int) $rows,
esc_attr('statusapi-copy-' . md5($title . $content)),
esc_textarea($content)
);
echo '
';
}
private static function render_status_card($title, $value, $meta, $status) {
self::render_status_card_named('', $title, $value, $meta, $status);
}
private static function render_status_card_named($id, $title, $value, $meta, $status) {
$status_class = 'statusapi-state-unknown';
if ($status === 'ok') {
$status_class = 'statusapi-state-ok';
} elseif ($status === 'error') {
$status_class = 'statusapi-state-error';
}
$id_attr = $id !== '' ? ' id="' . esc_attr($id) . '"' : '';
echo '
';
echo '' . esc_html($title) . '';
echo '' . esc_html($value) . '';
echo '' . esc_html($meta) . '';
echo '
';
}
private static function render_metric_card($id, $title, $value, $meta) {
echo '
';
echo '' . esc_html($title) . '';
echo '' . esc_html($value) . '';
echo '' . esc_html($meta) . '';
echo '
';
}
private static function build_curl_examples($status_endpoint, $attack_endpoint, $attack_api_key, $attack_source) {
$payload = wp_json_encode(array(
'event' => 'detected',
'source' => $attack_source,
'connectionsPerSecond' => 250,
'ipAddressesBlocked' => 12,
'connectionsBlocked' => 1800,
));
return "GET {$status_endpoint}\n\nPOST {$attack_endpoint}\nHeader: X-API-Key: {$attack_api_key}\nBody: {$payload}";
}
private static function probe_statusapi($base_url) {
$state = self::get_state();
$previous_status = isset($state['last_status']) ? (string) $state['last_status'] : 'unknown';
$previous_attack_mode = array_key_exists('last_attack_mode', $state) ? $state['last_attack_mode'] : null;
$previous_memory_high = !empty($state['last_memory_high']);
$previous_player_high = !empty($state['last_player_high']);
if ($base_url === '') {
return array(
'status' => 'unknown',
'label' => 'Nicht konfiguriert',
'meta' => 'Bitte zuerst eine StatusAPI Basis-URL eintragen.',
'http_code' => 0,
'checked_at_human' => self::format_timestamp($state['last_check_at']),
'metrics' => self::empty_metrics(),
);
}
$response = wp_remote_get($base_url, array('timeout' => 5));
$now = time();
if (is_wp_error($response)) {
$state['last_check_at'] = $now;
$state['last_http_code'] = 0;
$state['last_status'] = 'error';
$state['last_error'] = $response->get_error_message();
if ($previous_status !== 'error') {
self::push_message($state, 'error', 'Proxy offline', 'StatusAPI ist nicht erreichbar: ' . $response->get_error_message());
}
self::save_state($state);
return array(
'status' => 'error',
'label' => 'Offline',
'meta' => $response->get_error_message(),
'http_code' => 0,
'checked_at_human' => self::format_timestamp($now),
'metrics' => self::empty_metrics(),
);
}
$code = (int) wp_remote_retrieve_response_code($response);
$ok = $code >= 200 && $code < 300;
$body = wp_remote_retrieve_body($response);
$decoded = json_decode($body, true);
$state['last_check_at'] = $now;
$state['last_http_code'] = $code;
$state['last_status'] = $ok ? 'ok' : 'error';
$state['last_error'] = $ok ? '' : 'HTTP ' . $code;
if ($ok && $previous_status === 'error') {
self::push_message($state, 'success', 'Proxy wieder online', 'StatusAPI antwortet wieder erfolgreich.');
}
if (!$ok && $previous_status !== 'error') {
self::push_message($state, 'error', 'Proxy-Fehler', 'StatusAPI antwortet mit HTTP ' . $code . '.');
}
if ($ok && is_array($decoded)) {
$network = isset($decoded['network']) && is_array($decoded['network']) ? $decoded['network'] : array();
$antibot = isset($decoded['antibot']) && is_array($decoded['antibot']) ? $decoded['antibot'] : array();
if (array_key_exists('attack_mode', $antibot)) {
$attack_mode = (bool) $antibot['attack_mode'];
if ($previous_attack_mode !== null && $attack_mode !== (bool) $previous_attack_mode) {
if ($attack_mode) {
$cps = isset($antibot['last_cps']) ? $antibot['last_cps'] : 'n/a';
self::push_message($state, 'warning', 'Attack erkannt', 'AntiBot hat den Attack-Mode aktiviert (CPS: ' . $cps . ').');
} else {
self::push_message($state, 'success', 'Attack beendet', 'AntiBot hat den Attack-Mode beendet.');
}
}
$state['last_attack_mode'] = $attack_mode;
}
$memory_percent = null;
if (isset($network['memory']) && is_array($network['memory']) && isset($network['memory']['usage_percent'])) {
$memory_percent = (int) $network['memory']['usage_percent'];
}
$is_memory_high = $memory_percent !== null && $memory_percent >= 90;
if ($is_memory_high && !$previous_memory_high) {
self::push_message($state, 'warning', 'RAM-Warnung', 'RAM-Auslastung liegt bei ' . $memory_percent . '%.');
}
if (!$is_memory_high && $previous_memory_high) {
self::push_message($state, 'success', 'RAM normalisiert', 'RAM-Auslastung ist wieder unter 90%.');
}
$state['last_memory_high'] = $is_memory_high;
$occupancy = null;
if (isset($network['players']) && is_array($network['players']) && isset($network['players']['occupancy_percent'])) {
$occupancy = (int) $network['players']['occupancy_percent'];
}
$is_player_high = $occupancy !== null && $occupancy >= 95;
if ($is_player_high && !$previous_player_high) {
self::push_message($state, 'warning', 'Spieler-Auslastung hoch', 'Spieler-Auslastung liegt bei ' . $occupancy . '%.');
}
if (!$is_player_high && $previous_player_high) {
self::push_message($state, 'success', 'Spieler-Auslastung normalisiert', 'Spieler-Auslastung ist wieder unter 95%.');
}
$state['last_player_high'] = $is_player_high;
}
if ($ok) {
$state['last_success_at'] = $now;
$state['last_success_code'] = $code;
}
self::save_state($state);
return array(
'status' => $ok ? 'ok' : 'error',
'label' => $ok ? 'Online' : 'Fehler',
'meta' => $ok ? 'Antwort von ' . untrailingslashit($base_url) : 'HTTP ' . $code,
'http_code' => $code,
'checked_at_human' => self::format_timestamp($now),
'metrics' => $ok ? self::extract_metrics($decoded) : self::empty_metrics(),
);
}
private static function extract_metrics($decoded) {
if (!is_array($decoded)) {
return self::empty_metrics();
}
$network = isset($decoded['network']) && is_array($decoded['network']) ? $decoded['network'] : array();
$players = isset($network['players']) && is_array($network['players']) ? $network['players'] : array();
$memory = isset($network['memory']) && is_array($network['memory']) ? $network['memory'] : array();
$online_players = self::array_value($players, 'online', self::count_players_fallback($decoded));
$max_players = self::array_value($players, 'max', self::array_value($decoded, 'max_players', 'n/a'));
$occupancy = self::array_value($players, 'occupancy_percent', null);
$uptime_human = self::array_value($network, 'uptime_human', 'n/a');
$uptime_seconds = self::array_value($network, 'uptime_seconds', null);
$memory_percent = self::array_value($memory, 'usage_percent', 'n/a');
$memory_used = self::array_value($memory, 'used_mb', 'n/a');
$memory_max = self::array_value($memory, 'max_mb', 'n/a');
return array(
'online_players' => (string) $online_players,
'online_meta' => $occupancy !== null ? 'Auslastung ' . $occupancy . '%' : 'Aktuell verbundene Spieler',
'max_players' => (string) $max_players,
'max_meta' => 'Player-Limit laut Proxy',
'uptime' => (string) $uptime_human,
'uptime_meta' => $uptime_seconds !== null ? ((string) $uptime_seconds) . ' Sekunden' : 'Keine Uptime-Daten',
'memory' => ((string) $memory_percent) . '%',
'memory_meta' => $memory_used . ' MB von ' . $memory_max . ' MB',
);
}
private static function empty_metrics() {
return array(
'online_players' => 'n/a',
'online_meta' => 'Keine Live-Daten',
'max_players' => 'n/a',
'max_meta' => 'Keine Live-Daten',
'uptime' => 'n/a',
'uptime_meta' => 'Keine Live-Daten',
'memory' => 'n/a',
'memory_meta' => 'Keine Live-Daten',
);
}
private static function array_value($array, $key, $fallback) {
if (!is_array($array) || !array_key_exists($key, $array)) {
return $fallback;
}
return $array[$key];
}
private static function count_players_fallback($decoded) {
if (isset($decoded['players']) && is_array($decoded['players'])) {
return count($decoded['players']);
}
return 'n/a';
}
private static function format_timestamp($timestamp) {
$timestamp = (int) $timestamp;
if ($timestamp <= 0) {
return 'Noch kein Wert';
}
return wp_date('d.m.Y H:i:s', $timestamp);
}
private static function build_notice() {
if (!isset($_GET['statusapi_notice'])) {
return null;
}
$code = sanitize_text_field(wp_unslash($_GET['statusapi_notice']));
if ($code === 'connection_ok') {
return array('type' => 'success', 'message' => 'StatusAPI ist erreichbar.');
}
if ($code === 'attack_ok') {
return array('type' => 'success', 'message' => 'Test Attack-Meldung wurde erfolgreich an den Proxy gesendet.');
}
if ($code === 'missing_url') {
return array('type' => 'error', 'message' => 'Bitte zuerst die StatusAPI Basis-URL eintragen.');
}
if ($code === 'missing_attack_key') {
return array('type' => 'error', 'message' => 'Bitte zuerst einen Attack API-Key speichern.');
}
if ($code === 'connection_failed') {
return array('type' => 'error', 'message' => 'Verbindungstest fehlgeschlagen. Bitte Basis-URL und Erreichbarkeit prüfen.');
}
if ($code === 'attack_failed') {
return array('type' => 'error', 'message' => 'Test Attack-Meldung konnte nicht gesendet werden. API-Key und Proxy-Endpunkt prüfen.');
}
if ($code === 'messages_cleared') {
return array('type' => 'success', 'message' => 'Meldungen wurden erfolgreich geleert.');
}
return null;
}
public static function handle_test_action() {
if (!current_user_can('manage_options')) {
wp_die('Nicht erlaubt.');
}
check_admin_referer('statusapi_backend_helper_test');
$options = self::get_options();
$base_url = $options['statusapi_base_url'];
if ($base_url === '') {
self::redirect_with_notice('missing_url');
}
if (isset($_POST['test_connection'])) {
$ok = self::request_ok($base_url);
$state = self::get_state();
self::push_message(
$state,
$ok ? 'success' : 'error',
$ok ? 'Verbindungstest erfolgreich' : 'Verbindungstest fehlgeschlagen',
$ok ? 'Manueller Verbindungstest im WordPress-Backend war erfolgreich.' : 'Manueller Verbindungstest im WordPress-Backend war nicht erfolgreich.'
);
self::save_state($state);
self::redirect_with_notice($ok ? 'connection_ok' : 'connection_failed');
}
if (isset($_POST['test_attack'])) {
if ($options['attack_api_key'] === '') {
self::redirect_with_notice('missing_attack_key');
}
$payload = array(
'event' => 'detected',
'source' => $options['attack_source'] !== '' ? $options['attack_source'] : 'WordPress',
'connectionsPerSecond' => 250,
'ipAddressesBlocked' => 12,
'connectionsBlocked' => 1800,
);
$response = wp_remote_post($base_url . '/network/attack', array(
'timeout' => 8,
'headers' => array(
'Content-Type' => 'application/json; charset=utf-8',
'X-API-Key' => $options['attack_api_key'],
),
'body' => wp_json_encode($payload),
));
if (is_wp_error($response)) {
$state = self::get_state();
$state['last_check_at'] = time();
$state['last_http_code'] = 0;
$state['last_status'] = 'error';
$state['last_error'] = $response->get_error_message();
self::push_message($state, 'error', 'Test-Attack fehlgeschlagen', 'Konnte keine Test-Meldung senden: ' . $response->get_error_message());
self::save_state($state);
self::redirect_with_notice('attack_failed');
}
$code = (int) wp_remote_retrieve_response_code($response);
$state = self::get_state();
$state['last_check_at'] = time();
$state['last_http_code'] = $code;
$state['last_status'] = ($code >= 200 && $code < 300) ? 'ok' : 'error';
$state['last_error'] = ($code >= 200 && $code < 300) ? '' : 'HTTP ' . $code;
if ($code >= 200 && $code < 300) {
$state['last_success_at'] = $state['last_check_at'];
$state['last_success_code'] = $code;
self::push_message($state, 'success', 'Test-Attack gesendet', 'Die Test-Attack-Meldung wurde erfolgreich an den Proxy gesendet.');
} else {
self::push_message($state, 'error', 'Test-Attack fehlgeschlagen', 'Der Proxy antwortete mit HTTP ' . $code . '.');
}
self::save_state($state);
self::redirect_with_notice($code >= 200 && $code < 300 ? 'attack_ok' : 'attack_failed');
}
self::redirect_with_notice('connection_failed');
}
public static function handle_clear_messages() {
if (!current_user_can('manage_options')) {
wp_die('Nicht erlaubt.');
}
check_admin_referer('statusapi_backend_helper_clear_messages');
$state = self::get_state();
$state['messages'] = array();
self::save_state($state);
$redirect_page = isset($_POST['redirect_page']) ? sanitize_text_field(wp_unslash($_POST['redirect_page'])) : self::MENU_SLUG;
if ($redirect_page !== self::MESSAGES_SLUG) {
$redirect_page = self::MENU_SLUG;
}
self::redirect_with_notice('messages_cleared', $redirect_page);
}
public static function handle_ajax_refresh() {
if (!current_user_can('manage_options')) {
wp_send_json_error(array('message' => 'forbidden'), 403);
}
check_ajax_referer('statusapi_backend_helper_refresh');
$options = self::get_options();
$live_status = self::probe_statusapi($options['statusapi_base_url']);
$state = self::get_state();
wp_send_json_success(array(
'refresh_state' => 'Aktualisiert ' . self::format_timestamp($state['last_check_at']),
'messages_html' => self::build_messages_markup($state),
'status_cards' => array(
'proxy' => array(
'value' => $live_status['label'],
'meta' => $live_status['meta'],
'status' => $live_status['status'],
),
'http' => array(
'value' => $live_status['http_code'] > 0 ? (string) $live_status['http_code'] : 'n/a',
'meta' => $live_status['checked_at_human'],
'status' => $live_status['status'],
),
'success' => array(
'value' => self::format_timestamp($state['last_success_at']),
'meta' => $state['last_success_code'] > 0 ? 'HTTP ' . $state['last_success_code'] : 'Noch kein erfolgreicher Check gespeichert',
'status' => $state['last_success_at'] > 0 ? 'ok' : 'unknown',
),
'lastcheck' => array(
'value' => self::format_timestamp($state['last_check_at']),
'meta' => $state['last_error'] !== '' ? $state['last_error'] : 'Keine Fehler gespeichert',
'status' => $state['last_status'],
),
),
'metric_cards' => array(
'online' => array(
'value' => $live_status['metrics']['online_players'],
'meta' => $live_status['metrics']['online_meta'],
),
'max' => array(
'value' => $live_status['metrics']['max_players'],
'meta' => $live_status['metrics']['max_meta'],
),
'uptime' => array(
'value' => $live_status['metrics']['uptime'],
'meta' => $live_status['metrics']['uptime_meta'],
),
'memory' => array(
'value' => $live_status['metrics']['memory'],
'meta' => $live_status['metrics']['memory_meta'],
),
),
));
}
private static function request_ok($url) {
$response = wp_remote_get($url, array('timeout' => 8));
if (is_wp_error($response)) {
return false;
}
$code = (int) wp_remote_retrieve_response_code($response);
return $code >= 200 && $code < 300;
}
private static function redirect_with_notice($code, $page = self::MENU_SLUG) {
$target_page = ($page === self::MESSAGES_SLUG) ? self::MESSAGES_SLUG : self::MENU_SLUG;
$url = add_query_arg(
array(
'page' => $target_page,
'statusapi_notice' => $code,
),
admin_url('admin.php')
);
wp_safe_redirect($url);
exit;
}
private static function render_styles() {
echo '';
}
private static function render_scripts($refresh_nonce) {
echo '';
}
}
StatusAPI_Backend_Helper::boot();
}