first commit

This commit is contained in:
nix
2026-05-16 11:10:19 +02:00
commit 509c9b3737
172 changed files with 14496 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: 0');
ini_set('display_errors', 0);
error_reporting(0);
define('CRAFTY_URL', 'https://craft.ipv6.nix.roulaise.net');
define('CRAFTY_TOKEN', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJpYXQiOjE3NzczMDIwNjIsInRva2VuX2lkIjoxfQ.DXFKO4aim-nJ_Wyn-o3qMaVyv6AP3aCoAYukR-uQels');
// Ports connus → clé logique
const PORT_MAP = [9191 => 'mc1', 9292 => 'mc2'];
function craftyGet(string $path): ?array {
$ch = curl_init(CRAFTY_URL . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . CRAFTY_TOKEN],
]);
$body = curl_exec($ch);
curl_close($ch);
return $body ? json_decode($body, true) : null;
}
// Extraire -Xmx depuis la commande d'exécution → retourne en Mio
function extractXmx(string $cmd): ?int {
if (preg_match('/-Xmx(\d+)([GgMm])/i', $cmd, $m)) {
return strtolower($m[2]) === 'g'
? (int)$m[1] * 1024
: (int)$m[1];
}
return null;
}
// 1. Lister les serveurs
$list = craftyGet('/api/v2/servers');
if (!$list || ($list['status'] ?? '') !== 'ok') {
echo json_encode(['success' => false, 'error' => 'Crafty unreachable']);
exit;
}
$result = [];
foreach ($list['data'] as $srv) {
$uuid = $srv['server_id'];
// Stats de ce serveur
$stats = craftyGet("/api/v2/servers/{$uuid}/stats");
if (!$stats || ($stats['status'] ?? '') !== 'ok') continue;
$d = $stats['data'];
// Identifier la clé logique par port
$port = (int)($d['server_port'] ?? $srv['server_port'] ?? 0);
$key = PORT_MAP[$port] ?? null;
// Fallback par nom si port inconnu
if (!$key) {
$name = strtolower($d['server_name'] ?? $srv['server_name'] ?? '');
if (str_contains($name, '#1') || str_contains($name, 'mc1') || str_contains($name, 'forge')) $key = 'mc1';
elseif (str_contains($name, '#2') || str_contains($name, 'mc2') || str_contains($name, 'fabric')) $key = 'mc2';
else $key = $uuid;
}
// RAM utilisée : Crafty retourne en octets
$ramUsed = isset($d['mem']) ? (int)round($d['mem'] / 1024 / 1024) : null;
// RAM allouée : extraire -Xmx depuis execution_command
$cmd = $d['execution_command'] ?? $srv['execution_command'] ?? '';
$ramAlloc = extractXmx($cmd);
$result[$key] = [
'uuid' => $uuid,
'name' => $d['server_name'] ?? $srv['server_name'] ?? $uuid,
'port' => $port,
'running' => (bool)($d['running'] ?? false),
'players' => (int)($d['online'] ?? 0),
'max_players' => (int)($d['max'] ?? 0),
'ram_used' => $ramUsed,
'ram_alloc' => $ramAlloc,
'cpu' => isset($d['cpu']) ? round((float)$d['cpu'], 1) : null,
];
}
echo json_encode(['success' => true, 'servers' => $result], JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE);
?>
+115
View File
@@ -0,0 +1,115 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
ini_set('display_errors', 0);
error_reporting(0);
$result = [];
// ===== EVENTS (logs parsés par mc-log-watcher) =====
$logsFile = '/var/cache/mc-logs.json';
if (file_exists($logsFile)) {
$raw = @file_get_contents($logsFile);
$decoded = $raw ? @json_decode($raw, true) : null;
$result['events'] = $decoded['events'] ?? [];
// Renvoyer les N derniers triés du plus récent au plus ancien
$result['events'] = array_reverse(array_slice($result['events'], -100));
} else {
$result['events'] = [];
}
// ===== WHITELIST =====
$whitelists = [
'mc1' => '/srv/Minecraft/whitelist.json',
'mc2' => '/srv/Fabric-1.21.8/whitelist.json',
];
$result['whitelist'] = [];
foreach ($whitelists as $key => $path) {
if (file_exists($path)) {
$raw = @file_get_contents($path);
$players = $raw ? @json_decode($raw, true) : [];
$result['whitelist'][$key] = is_array($players) ? $players : [];
} else {
$result['whitelist'][$key] = null; // null = whitelist désactivée / fichier absent
}
}
// ===== BANNISSEMENTS =====
$banlists = [
'mc1' => '/srv/Minecraft/banned-players.json',
'mc2' => '/srv/Fabric-1.21.8/banned-players.json',
];
$result['banned'] = [];
foreach ($banlists as $key => $path) {
if (file_exists($path)) {
$raw = @file_get_contents($path);
$players = $raw ? @json_decode($raw, true) : [];
$result['banned'][$key] = is_array($players) ? $players : [];
} else {
$result['banned'][$key] = [];
}
}
// ===== MODS (Fabric uniquement - dossier mods/) =====
$modsDir = '/srv/Fabric-1.21.8/mods';
$result['mods'] = [];
if (is_dir($modsDir)) {
$files = @scandir($modsDir);
if ($files) {
foreach ($files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) === 'jar') {
// Tenter de lire fabric.mod.json à l'intérieur du jar (zip)
$jarPath = $modsDir . '/' . $file;
$modInfo = ['filename' => $file, 'name' => null, 'version' => null, 'description' => null];
// Lire le fabric.mod.json depuis le jar (zip)
if (class_exists('ZipArchive')) {
$zip = new ZipArchive();
if ($zip->open($jarPath) === true) {
$idx = $zip->locateName('fabric.mod.json');
if ($idx !== false) {
$json = @json_decode($zip->getFromIndex($idx), true);
if ($json) {
$modInfo['name'] = $json['name'] ?? $json['id'] ?? null;
$modInfo['version'] = $json['version'] ?? null;
$modInfo['description'] = $json['description'] ?? null;
$modInfo['id'] = $json['id'] ?? null;
}
}
$zip->close();
}
}
// Fallback: nom depuis le fichier
if (!$modInfo['name']) {
$modInfo['name'] = preg_replace('/[-_][\d.]+.*\.jar$/', '', $file);
}
$result['mods'][] = $modInfo;
}
}
// Trier par nom
usort($result['mods'], fn($a, $b) => strcasecmp($a['name'] ?? '', $b['name'] ?? ''));
}
}
// ===== SERVER PROPERTIES (whitelist activée ?) =====
$propsFiles = [
'mc1' => '/srv/Minecraft/server.properties',
'mc2' => '/srv/Fabric-1.21.8/server.properties',
];
$result['whitelist_enabled'] = [];
foreach ($propsFiles as $key => $path) {
$result['whitelist_enabled'][$key] = false;
if (file_exists($path)) {
$content = @file_get_contents($path);
if ($content && preg_match('/^white-list\s*=\s*true/m', $content)) {
$result['whitelist_enabled'][$key] = true;
}
}
}
echo json_encode(['success' => true, 'data' => $result], JSON_UNESCAPED_UNICODE);
?>
+32
View File
@@ -0,0 +1,32 @@
<?php
header('Content-Type: application/json');
header('Cache-Control: no-store');
$content = @file_get_contents('/proc/mdstat');
if (!$content) { echo json_encode(['error' => 'unreadable']); exit; }
$arrays = [];
$current = null;
foreach (explode("\n", $content) as $line) {
if (preg_match('/^(md\d+)\s*:\s*(\w+)\s+(\w+)\s+(.+)$/', $line, $m)) {
$current = $m[1];
$arrays[$current] = ['name' => $m[1], 'level' => $m[3], 'health' => 'ok', 'bitmap' => '', 'detail' => ''];
}
if ($current && preg_match('/\[(\d+)\/(\d+)\]\s*\[([U_]+)\]/', $line, $m)) {
$arrays[$current]['bitmap'] = $m[3];
$arrays[$current]['active'] = (int)$m[2];
$arrays[$current]['total'] = (int)$m[1];
if ((int)$m[2] < (int)$m[1] || str_contains($m[3], '_'))
$arrays[$current]['health'] = 'degraded';
}
if ($current && preg_match('/(resync|recovery)\s*=\s*([\d.]+%)/i', $line, $m)) {
$arrays[$current]['health'] = 'recovering';
$arrays[$current]['detail'] = "{$m[1]} {$m[2]}";
}
}
$list = array_values($arrays);
$healthy = !array_filter($list, fn($a) => $a['health'] !== 'ok');
echo json_encode(['healthy' => $healthy, 'arrays' => $list]);
+178
View File
@@ -0,0 +1,178 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: 0');
ini_set('display_errors', 0);
error_reporting(0);
/**
* Minecraft Server List Ping (protocole 1.7+)
* Ref: https://wiki.vg/Server_List_Ping
*/
function pingMinecraftServer(string $host, int $port = 25565, int $timeout = 4): array {
$result = [
'online' => false,
'players' => 0,
'max_players' => 0,
'version' => '',
'motd' => '',
'latency' => null,
];
$start = microtime(true);
$socket = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$socket) {
return $result;
}
stream_set_timeout($socket, $timeout);
// ---- Handshake packet (0x00) ----
$hostBytes = $host;
$hostLen = strlen($hostBytes);
$handshakeData = "\x00"; // Packet ID 0x00
$handshakeData .= varInt(47); // Protocol version (47 = 1.8, -1 non-spécifique)
$handshakeData .= varInt($hostLen) . $hostBytes; // Server address
$handshakeData .= pack('n', $port); // Port (unsigned short big-endian)
$handshakeData .= varInt(1); // Next state: 1 = status
fwrite($socket, varInt(strlen($handshakeData)) . $handshakeData);
// ---- Status Request (0x00, longueur 1) ----
fwrite($socket, "\x01\x00");
// ---- Lire la réponse ----
$packetLen = readVarInt($socket);
if ($packetLen <= 0) {
fclose($socket);
return $result;
}
// Lire l'ID du paquet (doit être 0x00)
readVarInt($socket);
// Lire la longueur de la chaîne JSON
$strLen = readVarInt($socket);
if ($strLen <= 0) {
fclose($socket);
return $result;
}
// Lire le JSON
$json = '';
$remaining = $strLen;
while ($remaining > 0 && !feof($socket)) {
$chunk = fread($socket, min($remaining, 4096));
if ($chunk === false || $chunk === '') break;
$json .= $chunk;
$remaining -= strlen($chunk);
}
fclose($socket);
$latency = (int) round((microtime(true) - $start) * 1000);
$data = @json_decode($json, true);
if (!$data) {
return $result;
}
// ---- Extraire le MOTD ----
$motd = '';
if (isset($data['description'])) {
if (is_string($data['description'])) {
$motd = $data['description'];
} elseif (is_array($data['description'])) {
$motd = $data['description']['text'] ?? '';
// Concatener les extras
if (!empty($data['description']['extra']) && is_array($data['description']['extra'])) {
foreach ($data['description']['extra'] as $extra) {
$motd .= $extra['text'] ?? '';
}
}
}
}
// Supprimer les codes couleur §X
$motd = preg_replace('/§[0-9a-fk-orA-FK-OR]/u', '', $motd);
$motd = trim(preg_replace('/\s+/', ' ', $motd));
$result['online'] = true;
$result['players'] = (int) ($data['players']['online'] ?? 0);
$result['max_players'] = (int) ($data['players']['max'] ?? 0);
$result['version'] = $data['version']['name'] ?? '';
$result['motd'] = $motd;
$result['latency'] = $latency;
return $result;
}
/**
* Encode un entier en VarInt Minecraft
*/
function varInt(int $value): string {
$out = '';
do {
$byte = $value & 0x7F;
$value = ($value >> 7) & PHP_INT_MAX; // shift sans signe
if ($value !== 0) $byte |= 0x80;
$out .= chr($byte);
} while ($value !== 0);
return $out;
}
/**
* Lit un VarInt depuis un socket
*/
function readVarInt($socket): int {
$value = 0;
$shift = 0;
do {
$raw = fgetc($socket);
if ($raw === false) return 0;
$byte = ord($raw);
$value |= ($byte & 0x7F) << $shift;
$shift += 7;
if ($shift > 35) break; // sécurité anti-boucle infinie
} while ($byte & 0x80);
return $value;
}
// ======================================================
// Configuration des serveurs à pinger
// ======================================================
$servers = [
[
'name' => 'Serveur MC #1',
'host' => 'nix.roulaise.net',
'port' => 9191,
],
[
'name' => 'Serveur MC #2',
'host' => 'nix.roulaise.net',
'port' => 9292,
],
];
// ======================================================
// Ping tous les serveurs et retourner le résultat JSON
// ======================================================
$results = [];
foreach ($servers as $srv) {
$ping = pingMinecraftServer($srv['host'], $srv['port']);
$results[] = array_merge([
'name' => $srv['name'],
'host' => $srv['host'],
'port' => $srv['port'],
], $ping);
}
echo json_encode([
'success' => true,
'servers' => $results,
'timestamp' => time(),
], JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK);
?>
+40
View File
@@ -0,0 +1,40 @@
<?php
// --- Configuration des serveurs ---
$servers = [
'Fabric 1.19.2' => '/srv/fabric-1.19.2',
'Forge 1.19.2' => '/srv/forge-1.19.2',
];
// ----------------------------------
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
function mod_name(string $filename): string {
$name = preg_replace('/\.jar$/i', '', $filename);
$name = preg_replace('/[-_](?:v?\d+[\.\d]*[\+\-\w]*).*$/i', '', $name);
$name = str_replace(['-', '_'], ' ', $name);
return ucwords(strtolower(trim($name)));
}
$result = [];
foreach ($servers as $label => $root) {
$mods_dir = $root . '/mods';
$entry = ['label' => $label, 'mods' => [], 'error' => null];
if (!is_dir($mods_dir)) {
$entry['error'] = 'Dossier introuvable : ' . $mods_dir;
} else {
$files = glob($mods_dir . '/*.jar');
sort($files);
foreach ($files as $f) {
$filename = basename($f);
$entry['mods'][] = [
'file' => $filename,
'name' => mod_name($filename),
];
}
}
$result[] = $entry;
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+133
View File
@@ -0,0 +1,133 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: 0');
ini_set('display_errors', 0);
error_reporting(0);
function getSystemStats() {
$stats = [];
// ========== UPTIME ==========
if (file_exists('/proc/uptime')) {
$uptime = @file_get_contents('/proc/uptime');
if ($uptime !== false) {
$uptimeSeconds = floatval(explode(' ', $uptime)[0]);
$days = floor($uptimeSeconds / 86400);
$hours = floor(($uptimeSeconds % 86400) / 3600);
$minutes = floor(($uptimeSeconds % 3600) / 60);
$stats['uptime'] = sprintf('%dd %dh %dm', $days, $hours, $minutes);
} else {
$stats['uptime'] = 'N/A';
}
} else {
$stats['uptime'] = 'N/A';
}
// ========== CPU USAGE ==========
if (file_exists('/proc/stat')) {
$stat1 = @file('/proc/stat');
if ($stat1 !== false && count($stat1) > 0) {
usleep(100000);
$stat2 = @file('/proc/stat');
if ($stat2 !== false && count($stat2) > 0) {
$info1 = explode(' ', preg_replace('!cpu +!', '', $stat1[0]));
$info2 = explode(' ', preg_replace('!cpu +!', '', $stat2[0]));
$dif = [
'user' => floatval($info2[0]) - floatval($info1[0]),
'nice' => floatval($info2[1]) - floatval($info1[1]),
'sys' => floatval($info2[2]) - floatval($info1[2]),
'idle' => floatval($info2[3]) - floatval($info1[3])
];
$total = array_sum($dif);
$stats['cpu_usage'] = $total > 0 ? round(100 - ($dif['idle'] / $total * 100), 1) : 0;
} else {
$load = sys_getloadavg();
$stats['cpu_usage'] = round(min($load[0] * 25, 100), 1);
}
} else {
$load = sys_getloadavg();
$stats['cpu_usage'] = round(min($load[0] * 25, 100), 1);
}
} else {
$stats['cpu_usage'] = 0;
}
// ========== MEMORY ==========
if (file_exists('/proc/meminfo')) {
$meminfo = @file_get_contents('/proc/meminfo');
if ($meminfo !== false) {
preg_match_all('/^(\w+):\s+(\d+)/m', $meminfo, $matches);
$mem = array_combine($matches[1], $matches[2]);
$memTotal = floatval($mem['MemTotal']) / 1024 / 1024;
$memAvailable = isset($mem['MemAvailable'])
? floatval($mem['MemAvailable'])
: (floatval($mem['MemFree']) + floatval($mem['Buffers']) + floatval($mem['Cached']));
$memFree = $memAvailable / 1024 / 1024;
$memUsed = $memTotal - $memFree;
$stats['ram_total'] = round($memTotal, 1);
$stats['ram_used'] = round($memUsed, 1);
$stats['ram_free'] = round($memFree, 1);
$stats['ram_percent'] = round(($memUsed / $memTotal) * 100, 1);
} else {
$stats['ram_total'] = $stats['ram_used'] = $stats['ram_percent'] = 0;
}
} else {
$stats['ram_total'] = $stats['ram_used'] = $stats['ram_percent'] = 0;
}
// ========== DISK ==========
$partitions = ['/', '/var', '/srv'];
foreach ($partitions as $partition) {
$key = str_replace('/', '', $partition);
if ($key === '') $key = 'root';
$diskTotal = @disk_total_space($partition);
$diskFree = @disk_free_space($partition);
if ($diskTotal !== false && $diskFree !== false) {
$diskUsed = $diskTotal - $diskFree;
$stats['disk_' . $key . '_total'] = round($diskTotal / 1024 / 1024 / 1024, 1);
$stats['disk_' . $key . '_used'] = round($diskUsed / 1024 / 1024 / 1024, 1);
$stats['disk_' . $key . '_free'] = round($diskFree / 1024 / 1024 / 1024, 1);
$stats['disk_' . $key . '_percent'] = round(($diskUsed / $diskTotal) * 100, 1);
} else {
$stats['disk_' . $key . '_total'] = 0;
$stats['disk_' . $key . '_free'] = 0;
$stats['disk_' . $key . '_percent'] = 0;
}
}
// ========== LOAD AVERAGE ==========
$loadavg = sys_getloadavg();
$stats['load_1'] = round($loadavg[0], 2);
$stats['load_5'] = round($loadavg[1], 2);
$stats['load_15'] = round($loadavg[2], 2);
// ========== PROCESSES ==========
if (function_exists('shell_exec')) {
$processes = @shell_exec('ps aux 2>/dev/null | wc -l');
$stats['processes'] = $processes !== null ? max(intval(trim($processes)) - 1, 0) : 0;
} else {
$stats['processes'] = 0;
}
$stats['hostname'] = @gethostname() ?: 'Unknown';
$stats['timestamp'] = time();
return $stats;
}
try {
echo json_encode([
'success' => true,
'data' => getSystemStats()
], JSON_NUMERIC_CHECK);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
?>