185 lines
6.0 KiB
PHP
185 lines
6.0 KiB
PHP
<?php
|
|
/**
|
|
* PercoHub API
|
|
* Retourne le catalogue des services avec leur statut en temps réel.
|
|
* - Docker containers : via `docker inspect`
|
|
* - Services HTTP (projets PHP) : via curl ping
|
|
*/
|
|
|
|
header('Content-Type: application/json');
|
|
header('Access-Control-Allow-Origin: *');
|
|
|
|
$action = $_GET['action'] ?? 'all';
|
|
|
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
|
|
function dockerStatus(string $name): array {
|
|
$raw = shell_exec("docker inspect --format '{{.State.Status}}|{{if .State.Health}}{{.State.Health.Status}}{{end}}' " . escapeshellarg($name) . " 2>/dev/null");
|
|
if (empty($raw)) return ['status' => 'absent', 'health' => null];
|
|
|
|
$parts = explode('|', trim($raw));
|
|
$status = $parts[0] ?? 'unknown';
|
|
$health = ($parts[1] ?? '') ?: null;
|
|
if ($health === '') $health = null;
|
|
|
|
return ['status' => $status, 'health' => $health];
|
|
}
|
|
|
|
function httpPing(string $url, int $timeout = 3): array {
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_NOBODY => true,
|
|
CURLOPT_TIMEOUT => $timeout,
|
|
CURLOPT_CONNECTTIMEOUT => $timeout,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
CURLOPT_SSL_VERIFYHOST => false,
|
|
CURLOPT_USERAGENT => 'PercoHub/1.0',
|
|
]);
|
|
curl_exec($ch);
|
|
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$time = (int) (curl_getinfo($ch, CURLINFO_TOTAL_TIME) * 1000);
|
|
curl_close($ch);
|
|
|
|
$ok = $code >= 200 && $code < 400;
|
|
return ['http_code' => $code, 'ok' => $ok, 'ms' => $time];
|
|
}
|
|
|
|
function resolveServiceStatus(array $service): array {
|
|
$dockerName = $service['docker'] ?? null;
|
|
$url = $service['url'] ?? null;
|
|
|
|
// Self-check: ce service est le dashboard lui-même → toujours online
|
|
if (!empty($service['self'])) {
|
|
return ['online' => true, 'status_text' => 'online', 'docker' => null, 'http' => null];
|
|
}
|
|
|
|
$result = [
|
|
'online' => false,
|
|
'status_text' => 'inconnu',
|
|
'docker' => null,
|
|
'http' => null,
|
|
];
|
|
|
|
// 1. Docker check
|
|
if ($dockerName) {
|
|
$d = dockerStatus($dockerName);
|
|
$result['docker'] = $d;
|
|
|
|
if ($d['status'] === 'running') {
|
|
$result['online'] = true;
|
|
$result['status_text'] = $d['health'] === 'healthy' ? 'healthy' : 'running';
|
|
} elseif ($d['status'] === 'absent') {
|
|
$result['status_text'] = 'absent';
|
|
} else {
|
|
$result['status_text'] = $d['status']; // exited, paused, restarting...
|
|
}
|
|
}
|
|
|
|
// 2. HTTP check (for PHP projects without docker, or in addition)
|
|
if ($url && !$dockerName) {
|
|
$h = httpPing($url);
|
|
$result['http'] = $h;
|
|
$result['online'] = $h['ok'];
|
|
$result['status_text'] = $h['ok'] ? 'online' : ($h['http_code'] > 0 ? "http {$h['http_code']}" : 'offline');
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
// ─── Routes ───────────────────────────────────────────────────────────────────
|
|
|
|
$configFile = __DIR__ . '/services.json';
|
|
if (!file_exists($configFile)) {
|
|
echo json_encode(['error' => 'services.json introuvable'], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$config = json_decode(file_get_contents($configFile), true);
|
|
if (!$config) {
|
|
echo json_encode(['error' => 'services.json invalide'], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'status') {
|
|
// Statut d'un seul service
|
|
$catId = $_GET['cat'] ?? null;
|
|
$svcIdx = (int)($_GET['idx'] ?? -1);
|
|
|
|
$found = null;
|
|
foreach ($config['categories'] as $cat) {
|
|
if ($cat['id'] === $catId && isset($cat['services'][$svcIdx])) {
|
|
$found = $cat['services'][$svcIdx];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$found) {
|
|
echo json_encode(['error' => 'service introuvable'], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
echo json_encode(resolveServiceStatus($found), JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'all') {
|
|
// Tous les services avec leur statut
|
|
$output = ['categories' => [], 'generated_at' => date('c'), 'total' => 0, 'online' => 0];
|
|
|
|
foreach ($config['categories'] as $cat) {
|
|
$catOut = [
|
|
'id' => $cat['id'],
|
|
'name' => $cat['name'],
|
|
'icon' => $cat['icon'],
|
|
'color' => $cat['color'],
|
|
'services' => [],
|
|
];
|
|
|
|
foreach ($cat['services'] as $svc) {
|
|
$statusInfo = resolveServiceStatus($svc);
|
|
|
|
$catOut['services'][] = array_merge($svc, [
|
|
'status' => $statusInfo,
|
|
]);
|
|
|
|
$output['total']++;
|
|
if ($statusInfo['online']) $output['online']++;
|
|
}
|
|
|
|
$output['categories'][] = $catOut;
|
|
}
|
|
|
|
$output['offline'] = $output['total'] - $output['online'];
|
|
|
|
echo json_encode($output, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'config') {
|
|
// Catalogue brut sans status checks
|
|
echo json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'docker') {
|
|
// Liste tous les containers Docker
|
|
$raw = shell_exec("docker ps -a --format '{{.Names}}|{{.Image}}|{{.Status}}|{{.Ports}}' 2>/dev/null");
|
|
$containers = [];
|
|
foreach (explode("\n", trim($raw)) as $line) {
|
|
if (empty($line)) continue;
|
|
$parts = explode('|', $line);
|
|
$containers[] = [
|
|
'name' => $parts[0] ?? '',
|
|
'image' => $parts[1] ?? '',
|
|
'status' => $parts[2] ?? '',
|
|
'ports' => $parts[3] ?? '',
|
|
];
|
|
}
|
|
echo json_encode(['containers' => $containers, 'count' => count($containers)], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
echo json_encode(['error' => 'action invalide. Utilisez: all, status, config, docker'], JSON_UNESCAPED_UNICODE);
|