Files
html/api/wtp-udock-coverage.php
Opus Wire 028d569108
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
feat(wtp-udock-100pct-TRUE): 313/313 = 100 pct COVERAGE
2026-04-21 14:12:58 +02:00

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);