- 134-tool registry with programmatic SEO (unique titles/H1/descriptions, JSON-LD graphs, sitemap index, canonical 301 enforcement via required filter) - DB-backed job queue (SKIP LOCKED) with drivers: Ffmpeg, Images (GD), Pdf (qpdf/gs/poppler), Youtube (thumbnails), Qr (server-side PNG) - Security: SSRF guard, MIME validation, rate limits, API-key auth, bcrypt admin login, security headers - Admin panel: dashboard, tools/categories/guides CRUD, SEO audit, analytics, job inspector with retry, system health, feature flags - Docker deployment (nginx + web/api FPM pools + scalable workers), PHPUnit suite (19 tests / 1139 assertions), PWA manifest + service worker
37 lines
2.1 KiB
PHP
37 lines
2.1 KiB
PHP
<?= view('partials/admin_nav') ?>
|
||
<h1 style="font-size:1.7rem;margin-bottom:1rem">Analytics (anonymous events only)</h1>
|
||
<div class="grid cols-4" style="margin-bottom:1.4rem">
|
||
<?php foreach (['tool_view' => 'Tool views', 'tool_start' => 'Jobs started', 'tool_success' => 'Successes', 'download' => 'Downloads'] as $key => $label): ?>
|
||
<div class="card"><p style="font-size:.78rem;text-transform:uppercase;color:var(--ink-500)"><?= esc($label) ?></p>
|
||
<h2 style="font-size:1.7rem;font-weight:800;margin:.15rem 0"><?= number_format($counts[$key] ?? 0) ?> <span class="hint">/24h</span></h2></div>
|
||
<?php endforeach ?>
|
||
</div>
|
||
|
||
<h2 class="section-title">Top tools (7 days)</h2>
|
||
<table style="width:100%;font-size:.87rem;border-collapse:collapse;background:#fff;border-radius:var(--radius-sm);box-shadow:var(--shadow-sm)">
|
||
<tr style="text-align:left;color:var(--ink-500)"><th style="padding:.65rem">Tool</th><th>Views</th><th>Starts</th><th>Success rate</th></tr>
|
||
<?php foreach ($topTools as $row): ?>
|
||
<tr style="border-top:1px solid #f2f2f5">
|
||
<td style="padding:.5rem .65rem"><a href="/<?= esc($row['tool_slug']) ?>"><?= esc(str_replace('-', ' ', $row['tool_slug'])) ?></a></td>
|
||
<td><?= number_format((float)$row['views']) ?></td>
|
||
<td><?= number_format((float)$row['starts']) ?></td>
|
||
<td><?= $row['success_rate'] === null ? '–' : $row['success_rate'] . '%' ?></td>
|
||
</tr>
|
||
<?php endforeach ?>
|
||
</table>
|
||
|
||
<div class="grid cols-2" style="margin-top:1.4rem">
|
||
<div class="card">
|
||
<h3>Output formats (7d)</h3>
|
||
<?php if ($formats === []): ?><p class="hint">No downloads yet.</p><?php else: ?>
|
||
<ul style="padding-left:1.1rem"><?php foreach ($formats as $f): ?><li><strong><?= esc(strtoupper($f['format'])) ?></strong> — <?= number_format((float)$f['total']) ?></li><?php endforeach ?></ul>
|
||
<?php endif ?>
|
||
</div>
|
||
<div class="card">
|
||
<h3>Recent searches</h3>
|
||
<?php if ($searches === []): ?><p class="hint">None yet.</p><?php else: ?>
|
||
<ul style="padding-left:1.1rem"><?php foreach ($searches as $s): $m = json_decode((string)($s['meta'] ?? '{}'), true); ?><li><?= esc($m['term'] ?? '?') ?></li><?php endforeach ?></ul>
|
||
<?php endif ?>
|
||
</div>
|
||
</div>
|