perf: cache session vision_check (15min) — zéro requête réseau par page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* vision.php — Intégration Vision (licence)
|
* vision.php — Intégration Vision (licence)
|
||||||
* API : GET https://vision.copydev.fr/api/license.php?key=<key>&machine_id=<id>&hostname=<host>
|
* Cache SESSION : aucune requête réseau pendant 15 min après le dernier check.
|
||||||
* Vérification toutes les 30 min, résultat mis en cache en BDD.
|
* Le check HTTP n'a lieu qu'une fois par session toutes les 15 minutes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define('VISION_LICENSE_URL', 'https://vision.copydev.fr/api/license.php');
|
define('VISION_LICENSE_URL', 'https://vision.copydev.fr/api/license.php');
|
||||||
define('VISION_PRODUCT', 'copymail');
|
define('VISION_PRODUCT', 'copymail');
|
||||||
define('VISION_CACHE_TTL', 1800); // 30 minutes
|
define('VISION_CACHE_TTL', 900); // 15 minutes
|
||||||
|
|
||||||
function vision_machine_id(): string {
|
function vision_machine_id(): string {
|
||||||
$id = cfg('vision_machine_id', '');
|
$id = cfg('vision_machine_id', '');
|
||||||
@@ -19,48 +19,42 @@ function vision_machine_id(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function vision_check(): array {
|
function vision_check(): array {
|
||||||
|
// 1. Cache session : si le résultat est frais, on retourne immédiatement
|
||||||
|
if (!empty($_SESSION['vision_result']) && !empty($_SESSION['vision_checked_at'])) {
|
||||||
|
if ((time() - $_SESSION['vision_checked_at']) < VISION_CACHE_TTL) {
|
||||||
|
return $_SESSION['vision_result'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Clé non configurée
|
||||||
$key = cfg('vision_license_key', '');
|
$key = cfg('vision_license_key', '');
|
||||||
if (!$key) {
|
if (!$key) {
|
||||||
return ['valid' => false, 'msg' => 'Licence non configurée.', 'unconfigured' => true];
|
$r = ['valid' => false, 'msg' => 'Licence non configurée.', 'unconfigured' => true];
|
||||||
}
|
$_SESSION['vision_result'] = $r;
|
||||||
|
$_SESSION['vision_checked_at'] = time();
|
||||||
// Cache : si dernier check < 30 min et résultat valide, on ne re-vérifie pas
|
return $r;
|
||||||
$lastCheck = (int)cfg('vision_last_check', '0');
|
|
||||||
$lastResult = cfg('vision_last_result', '');
|
|
||||||
if ($lastResult && (time() - $lastCheck) < VISION_CACHE_TTL) {
|
|
||||||
$cached = json_decode($lastResult, true);
|
|
||||||
if ($cached) return $cached;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. Appel Vision (max 3s timeout)
|
||||||
$url = VISION_LICENSE_URL . '?' . http_build_query([
|
$url = VISION_LICENSE_URL . '?' . http_build_query([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'machine_id' => vision_machine_id(),
|
'machine_id' => vision_machine_id(),
|
||||||
'hostname' => cfg('hostname', gethostname()),
|
'hostname' => cfg('hostname', gethostname()),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$ctx = stream_context_create(['http' => [
|
$ctx = stream_context_create(['http' => ['method' => 'GET', 'timeout' => 3, 'ignore_errors' => true]]);
|
||||||
'method' => 'GET',
|
|
||||||
'timeout' => 5,
|
|
||||||
'ignore_errors' => true,
|
|
||||||
]]);
|
|
||||||
|
|
||||||
$resp = @file_get_contents($url, false, $ctx);
|
$resp = @file_get_contents($url, false, $ctx);
|
||||||
|
|
||||||
if ($resp === false) {
|
if ($resp === false) {
|
||||||
// Vision injoignable — mode dégradé selon offline_timeout
|
// Vision injoignable — mode dégradé
|
||||||
$lastOk = (int)cfg('vision_last_ok', '0');
|
$lastOk = (int)cfg('vision_last_ok', '0');
|
||||||
$timeout = (int)cfg('vision_offline_timeout', '72');
|
$timeout = (int)cfg('vision_offline_timeout', '72');
|
||||||
$elapsed = (time() - $lastOk) / 3600;
|
$elapsed = $lastOk ? (time() - $lastOk) / 3600 : PHP_INT_MAX;
|
||||||
if ($lastOk && $elapsed < $timeout) {
|
$result = ($lastOk && $elapsed < $timeout)
|
||||||
$result = ['valid' => true, 'msg' => "Vision injoignable — mode dégradé ({$timeout}h max)", 'degraded' => true];
|
? ['valid' => true, 'msg' => "Vision injoignable — mode dégradé ({$timeout}h max)", 'degraded' => true]
|
||||||
|
: ['valid' => false, 'msg' => 'Vision injoignable et délai hors-ligne dépassé.'];
|
||||||
} else {
|
} else {
|
||||||
$result = ['valid' => false, 'msg' => 'Vision injoignable et délai hors-ligne dépassé.'];
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = json_decode($resp, true);
|
$data = json_decode($resp, true);
|
||||||
|
|
||||||
if (!empty($data['valid'])) {
|
if (!empty($data['valid'])) {
|
||||||
$result = [
|
$result = [
|
||||||
'valid' => true,
|
'valid' => true,
|
||||||
@@ -70,39 +64,30 @@ function vision_check(): array {
|
|||||||
'devices' => $data['max_devices'] ?? 1,
|
'devices' => $data['max_devices'] ?? 1,
|
||||||
];
|
];
|
||||||
cfg_set('vision_last_ok', (string)time());
|
cfg_set('vision_last_ok', (string)time());
|
||||||
cfg_set('vision_last_check', (string)time());
|
|
||||||
cfg_set('vision_last_result', json_encode($result));
|
|
||||||
cfg_set('vision_offline_timeout', (string)($data['offline_timeout_hours'] ?? 72));
|
cfg_set('vision_offline_timeout', (string)($data['offline_timeout_hours'] ?? 72));
|
||||||
return $result;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
// Licence invalide / désactivée depuis Vision : invalide immédiatement le cache
|
|
||||||
$result = ['valid' => false, 'msg' => $data['message'] ?? 'Licence invalide ou désactivée.'];
|
$result = ['valid' => false, 'msg' => $data['message'] ?? 'Licence invalide ou désactivée.'];
|
||||||
cfg_set('vision_last_check', (string)time());
|
cfg_set('vision_last_ok', '0');
|
||||||
cfg_set('vision_last_result', json_encode($result));
|
}
|
||||||
cfg_set('vision_last_ok', '0'); // reset grace period
|
}
|
||||||
|
|
||||||
|
// 4. Stocke en session
|
||||||
|
$_SESSION['vision_result'] = $result;
|
||||||
|
$_SESSION['vision_checked_at'] = time();
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie la licence et bloque si invalide.
|
|
||||||
* À appeler sur toutes les pages.
|
|
||||||
* Exception : la page de config (/admin/domain.php) reste accessible aux admins.
|
|
||||||
*/
|
|
||||||
function vision_enforce(): void {
|
function vision_enforce(): void {
|
||||||
$result = vision_check();
|
$result = vision_check();
|
||||||
if ($result['valid']) return;
|
if ($result['valid']) return;
|
||||||
|
|
||||||
$uri = $_SERVER['REQUEST_URI'] ?? '';
|
$uri = $_SERVER['REQUEST_URI'] ?? '';
|
||||||
|
|
||||||
// L'admin peut toujours accéder à la config pour saisir la clé
|
|
||||||
if (is_admin() && strpos($uri, '/admin/domain.php') !== false) return;
|
if (is_admin() && strpos($uri, '/admin/domain.php') !== false) return;
|
||||||
|
|
||||||
// Bloque tout le reste
|
|
||||||
$msg = htmlspecialchars($result['msg'] ?? 'Licence invalide.');
|
$msg = htmlspecialchars($result['msg'] ?? 'Licence invalide.');
|
||||||
$isAdmin = is_admin();
|
$configLink = is_admin()
|
||||||
$configLink = $isAdmin
|
? '<a href="/admin/domain.php" class="btn btn-outline-light mt-3"><i class="bi bi-key me-1"></i>Configurer la licence</a>'
|
||||||
? '<a href="/admin/domain.php" class="btn btn-outline-light mt-2"><i class="bi bi-key me-1"></i>Configurer la licence</a>'
|
|
||||||
: '';
|
: '';
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo "<!DOCTYPE html><html lang='fr'><head><meta charset='UTF-8'>
|
echo "<!DOCTYPE html><html lang='fr'><head><meta charset='UTF-8'>
|
||||||
|
|||||||
Reference in New Issue
Block a user