213 lines
9.3 KiB
PHP
213 lines
9.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App;
|
|
|
|
use PDO;
|
|
use Throwable;
|
|
|
|
require_once __DIR__ . '/ConfigGenerator.php';
|
|
require_once __DIR__ . '/AmiClient.php';
|
|
|
|
class SystemChecker
|
|
{
|
|
public static function runDiagnostics(PDO $db): array
|
|
{
|
|
$settings = $db->query('SELECT * FROM settings WHERE id = 1')->fetch() ?: [];
|
|
$leDomain = trim((string)($settings['letsencrypt_domain'] ?? ''));
|
|
|
|
$checks = [];
|
|
|
|
// 1. OS Debian & PHP SQLite3
|
|
$osPretty = 'Linux Debian';
|
|
if (is_readable('/etc/os-release')) {
|
|
$osContent = (string)file_get_contents('/etc/os-release');
|
|
if (preg_match('/PRETTY_NAME="([^"]+)"/', $osContent, $m)) {
|
|
$osPretty = $m[1];
|
|
}
|
|
}
|
|
$checks[] = [
|
|
'id' => 'debian_os',
|
|
'label' => 'Système Debian & PHP ' . PHP_VERSION,
|
|
'ok' => extension_loaded('pdo_sqlite'),
|
|
'detail' => $osPretty . ' (PDO SQLite actif)',
|
|
];
|
|
|
|
// 2. Base SQLite accessible en écriture
|
|
$dbWritable = is_writable(Database::DB_PATH) && is_writable(dirname(Database::DB_PATH));
|
|
$checks[] = [
|
|
'id' => 'sqlite_db',
|
|
'label' => 'Base de données SQLite3 (Source unique)',
|
|
'ok' => $dbWritable,
|
|
'detail' => $dbWritable
|
|
? 'Lecture/Écriture OK (' . realpath(Database::DB_PATH) . ')'
|
|
: 'Permissions insuffisantes sur storage/database.sqlite',
|
|
];
|
|
|
|
// 3. Utilisateur et Groupe Debian asterisk:asterisk
|
|
$astUser = function_exists('posix_getpwnam') ? posix_getpwnam('asterisk') : true;
|
|
$astGroup = function_exists('posix_getgrnam') ? posix_getgrnam('asterisk') : true;
|
|
$checks[] = [
|
|
'id' => 'asterisk_user',
|
|
'label' => 'Utilisateur & Groupe Debian (asterisk:asterisk)',
|
|
'ok' => (bool)($astUser && $astGroup),
|
|
'detail' => ($astUser && $astGroup)
|
|
? 'Utilisateur système asterisk:asterisk présent'
|
|
: 'Utilisateur ou groupe asterisk introuvable sur Debian',
|
|
];
|
|
|
|
// 4. Droits d'écriture sur /etc/asterisk et *.custom.conf
|
|
$etcWritable = is_dir('/etc/asterisk') && is_writable('/etc/asterisk');
|
|
$pjsipOk = !file_exists(ConfigGenerator::PJSIP_CONF_PATH) ? $etcWritable : is_writable(ConfigGenerator::PJSIP_CONF_PATH);
|
|
$extOk = !file_exists(ConfigGenerator::EXTENSIONS_CONF_PATH) ? $etcWritable : is_writable(ConfigGenerator::EXTENSIONS_CONF_PATH);
|
|
$permOk = $etcWritable && $pjsipOk && $extOk;
|
|
$checks[] = [
|
|
'id' => 'asterisk_perms',
|
|
'label' => 'Droits d\'écriture sur /etc/asterisk/*.custom.conf',
|
|
'ok' => $permOk,
|
|
'detail' => $permOk
|
|
? '/etc/asterisk accessible en écriture par le worker PHP (groupe asterisk)'
|
|
: 'Accès refusé en écriture dans /etc/asterisk pour PHP-FPM',
|
|
];
|
|
|
|
// 5. Directives #tryinclude dans pjsip.conf et extensions.conf
|
|
$pjsipMain = @file_get_contents('/etc/asterisk/pjsip.conf') ?: '';
|
|
$extMain = @file_get_contents('/etc/asterisk/extensions.conf') ?: '';
|
|
$incOk = str_contains($pjsipMain, 'pjsip.custom.conf') && str_contains($extMain, 'extensions.custom.conf');
|
|
$checks[] = [
|
|
'id' => 'asterisk_includes',
|
|
'label' => 'Inclusions natives (#tryinclude *.custom.conf)',
|
|
'ok' => $incOk,
|
|
'detail' => $incOk
|
|
? 'pjsip.conf et extensions.conf incluent bien les fichiers .custom.conf'
|
|
: 'Directive #tryinclude manquante dans pjsip.conf ou extensions.conf',
|
|
];
|
|
|
|
// 6. Certificats TLS (Let's Encrypt ou Certificat local Debian)
|
|
if ($leDomain !== '') {
|
|
$leCert = "/etc/letsencrypt/live/{$leDomain}/fullchain.pem";
|
|
$leKey = "/etc/letsencrypt/live/{$leDomain}/privkey.pem";
|
|
$leOk = is_readable($leCert) && is_readable($leKey);
|
|
$checks[] = [
|
|
'id' => 'tls_certs',
|
|
'label' => "Certificat TLS Let's Encrypt ({$leDomain})",
|
|
'ok' => $leOk,
|
|
'detail' => $leOk
|
|
? "Certificat Let's Encrypt lisible ({$leCert})"
|
|
: "Certificat introuvable ou non lisible dans /etc/letsencrypt/live/{$leDomain}/",
|
|
];
|
|
} else {
|
|
$fallbackOk = is_readable('/etc/asterisk/keys/asterisk.crt') && is_readable('/etc/asterisk/keys/asterisk.key');
|
|
$checks[] = [
|
|
'id' => 'tls_certs',
|
|
'label' => 'Certificats TLS SIP (Port 5061)',
|
|
'ok' => $fallbackOk,
|
|
'detail' => $fallbackOk
|
|
? 'Certificat TLS local actif (/etc/asterisk/keys/asterisk.crt) — prêt pour Let\'s Encrypt'
|
|
: 'Aucun domaine Let\'s Encrypt configuré',
|
|
];
|
|
}
|
|
|
|
// 7. Connexion Asterisk Manager Interface (AMI)
|
|
$amiOk = false;
|
|
$amiDetail = '';
|
|
try {
|
|
$ami = AmiClient::fromDatabase($db);
|
|
$info = $ami->testConnection();
|
|
$amiOk = true;
|
|
$amiDetail = ($info['version'] ?: $info['banner']) . ' (Socket AMI 127.0.0.1:' . ($settings['ami_port'] ?? 5038) . ')';
|
|
} catch (Throwable $e) {
|
|
$amiDetail = $e->getMessage();
|
|
}
|
|
|
|
$checks[] = [
|
|
'id' => 'ami_connection',
|
|
'label' => 'Liaison Asterisk Manager Interface (AMI)',
|
|
'ok' => $amiOk,
|
|
'detail' => $amiDetail,
|
|
];
|
|
|
|
$allCriticalOk = true;
|
|
foreach ($checks as $c) {
|
|
if (!$c['ok'] && $c['id'] !== 'tls_certs') {
|
|
$allCriticalOk = false;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'healthy' => $allCriticalOk,
|
|
'checks' => $checks,
|
|
];
|
|
}
|
|
|
|
public static function ensureAsteriskIncludes(): void
|
|
{
|
|
$pjsipConf = '/etc/asterisk/pjsip.conf';
|
|
if (is_file($pjsipConf) && is_writable($pjsipConf)) {
|
|
$content = (string)file_get_contents($pjsipConf);
|
|
if (!str_contains($content, 'pjsip.custom.conf')) {
|
|
file_put_contents($pjsipConf, rtrim($content) . "\n\n#tryinclude \"pjsip.custom.conf\"\n");
|
|
}
|
|
}
|
|
|
|
$extConf = '/etc/asterisk/extensions.conf';
|
|
if (is_file($extConf) && is_writable($extConf)) {
|
|
$content = (string)file_get_contents($extConf);
|
|
if (!str_contains($content, 'extensions.custom.conf')) {
|
|
file_put_contents($extConf, rtrim($content) . "\n\n#tryinclude \"extensions.custom.conf\"\n");
|
|
}
|
|
}
|
|
|
|
$mohConf = '/etc/asterisk/musiconhold.conf';
|
|
if (is_file($mohConf) && is_writable($mohConf)) {
|
|
$content = (string)file_get_contents($mohConf);
|
|
// Désactive la classe [default] native d'Asterisk pour laisser la priorité à musiconhold.custom.conf
|
|
$cleaned = preg_replace('/^\[default\]/m', ';[default]', $content);
|
|
$cleaned = preg_replace('/^mode=files/m', ';mode=files', $cleaned);
|
|
$cleaned = preg_replace('/^directory=moh/m', ';directory=moh', $cleaned);
|
|
if (!str_contains($cleaned, 'musiconhold.custom.conf')) {
|
|
$cleaned = rtrim($cleaned) . "\n\n#tryinclude \"/etc/asterisk/musiconhold.custom.conf\"\n";
|
|
}
|
|
if ($cleaned !== $content) {
|
|
file_put_contents($mohConf, $cleaned);
|
|
}
|
|
}
|
|
|
|
$vmConf = '/etc/asterisk/voicemail.conf';
|
|
if (is_file($vmConf) && is_writable($vmConf)) {
|
|
$content = (string)file_get_contents($vmConf);
|
|
$cleaned = preg_replace('/^1234 => 4242.*/m', ';1234 => 4242,Example Mailbox,root@localhost', $content);
|
|
$cleaned = preg_replace('/^;\s*passwordlocation\s*=\s*spooldir/m', 'passwordlocation=spooldir', $cleaned);
|
|
$cleaned = preg_replace('/^;\s*externpassnotify\s*=.*/m', 'externpassnotify=/usr/bin/php /var/www/nexus-pbx/scripts/voicemail_pass_sync.php', $cleaned);
|
|
if (!str_contains($cleaned, 'voicemail.custom.conf')) {
|
|
$cleaned = rtrim($cleaned) . "\n\n#tryinclude \"voicemail.custom.conf\"\n";
|
|
}
|
|
if ($cleaned !== $content) {
|
|
file_put_contents($vmConf, $cleaned);
|
|
}
|
|
}
|
|
|
|
$astConf = '/etc/asterisk/asterisk.conf';
|
|
if (is_file($astConf) && is_writable($astConf)) {
|
|
$content = (string)file_get_contents($astConf);
|
|
$lang = 'fr';
|
|
try {
|
|
$db = Database::getConnection();
|
|
$s = $db->query('SELECT system_language FROM settings WHERE id = 1')->fetch();
|
|
if (!empty($s['system_language'])) {
|
|
$lang = trim((string)$s['system_language']);
|
|
}
|
|
} catch (\Throwable $e) {}
|
|
if (preg_match('/^;?\s*defaultlanguage\s*=/m', $content)) {
|
|
$cleaned = preg_replace('/^;?\s*defaultlanguage\s*=.*$/m', "defaultlanguage = {$lang}", $content);
|
|
} else {
|
|
$cleaned = preg_replace('/\[options\]/i', "[options]\ndefaultlanguage = {$lang}", $content);
|
|
}
|
|
if ($cleaned !== $content) {
|
|
file_put_contents($astConf, $cleaned);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|