90 lines
3.3 KiB
PHP
90 lines
3.3 KiB
PHP
<?php
|
|
/* ═══════════════════════════════════════════════════════════════════
|
|
WTP UDOCK COVERAGE ENDPOINT · Opus 21-avr tour35
|
|
Scans /var/www/html/*.html and returns JSON coverage stats
|
|
Usage: GET /api/wtp-udock-coverage.php [?detail=1]
|
|
═══════════════════════════════════════════════════════════════════ */
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('Cache-Control: public, max-age=60');
|
|
header('X-WTP-UDOCK-V', '1');
|
|
|
|
$DIR = '/var/www/html';
|
|
$NAV_PATTERNS = ['wtp-unified-dock.js', 'opus-xlinks', 'wtp-sidebar', 'v130-xnav'];
|
|
$EXCLUDE = ['.bak', 'gold_'];
|
|
// SEO_VERIFICATION_EXEMPT - files that must NOT have dock (SEO verification tokens)
|
|
$SEO_EXEMPT = ['googlecba1a80ba979325c.html', 'google-site-verification.html'];
|
|
|
|
$files = glob($DIR . '/*.html') ?: [];
|
|
$total = 0;
|
|
$covered = 0;
|
|
$by_pattern = array_fill_keys($NAV_PATTERNS, 0);
|
|
$uncovered = [];
|
|
$chattr_i_count = 0;
|
|
|
|
$detail = isset($_GET['detail']) && $_GET['detail'] === '1';
|
|
|
|
foreach ($files as $f) {
|
|
$name = basename($f);
|
|
$skip = false;
|
|
foreach ($EXCLUDE as $ex) { if (strpos($name, $ex) !== false) { $skip = true; break; } }
|
|
// SEO exempt: don't count these in total (they must stay minimal)
|
|
if (in_array($name, $SEO_EXEMPT)) { $skip = true; }
|
|
if ($skip || strpos($name, '_') === 0) continue;
|
|
$total++;
|
|
|
|
$content = @file_get_contents($f);
|
|
if ($content === false) continue;
|
|
|
|
$has_nav = false;
|
|
foreach ($NAV_PATTERNS as $pat) {
|
|
if (strpos($content, $pat) !== false) {
|
|
$by_pattern[$pat]++;
|
|
$has_nav = true;
|
|
break; // count first matched pattern only (avoid double-count)
|
|
}
|
|
}
|
|
if ($has_nav) {
|
|
$covered++;
|
|
} else {
|
|
// Check if chattr+i
|
|
$is_immutable = false;
|
|
$lsattr_out = @shell_exec("lsattr " . escapeshellarg($f) . " 2>/dev/null");
|
|
if ($lsattr_out && preg_match('/^----i/', $lsattr_out)) {
|
|
$is_immutable = true;
|
|
$chattr_i_count++;
|
|
}
|
|
$size = @filesize($f);
|
|
$uncovered[] = [
|
|
'name' => $name,
|
|
'size' => $size,
|
|
'immutable' => $is_immutable,
|
|
'size_human' => $size ? round($size/1024, 1) . ' KB' : '?'
|
|
];
|
|
}
|
|
}
|
|
|
|
$response = [
|
|
'ts' => date('c'),
|
|
'source' => 'wtp-udock-coverage v1',
|
|
'total_pages' => $total,
|
|
'covered' => $covered,
|
|
'uncovered' => count($uncovered),
|
|
'coverage_pct' => $total > 0 ? round(100 * $covered / $total, 1) : 0,
|
|
'by_pattern' => [
|
|
'wtp-unified-dock.js' => $by_pattern['wtp-unified-dock.js'],
|
|
'opus-xlinks' => $by_pattern['opus-xlinks'],
|
|
'wtp-sidebar' => $by_pattern['wtp-sidebar'],
|
|
'v130-xnav' => $by_pattern['v130-xnav']
|
|
],
|
|
'immutable_blocked' => $chattr_i_count,
|
|
'target_100pct_remaining' => $total - $covered
|
|
];
|
|
|
|
if ($detail) {
|
|
// Sort uncovered by size desc, take top 30
|
|
usort($uncovered, function($a, $b) { return ($b['size'] ?? 0) - ($a['size'] ?? 0); });
|
|
$response['uncovered_top30'] = array_slice($uncovered, 0, 30);
|
|
}
|
|
|
|
echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|