- Editorial web platform built with Node.js, Express, EJS, and Vanilla CSS - Markdown content engine with YAML frontmatter, bilingual variants, and hot-reload watcher - Unified SQLite with FTS5 search indexing, query logging, and schema filters - Centered editorial post layout with margin-docked left TOC and mobile bottom sheet - WordPress-grade comment system with moderation dashboard and bulk operations under /guanli - Complete SEO suite (JSON-LD structured data, RSS/Atom feeds, XML sitemap, robots.txt) - CI/CD workflows for linting, security audits (Gitleaks), automated tests, and Docker releases
70 lines
4.1 KiB
Plaintext
70 lines
4.1 KiB
Plaintext
<%- include('../partials/head', { pageType: 'admin' }) %>
|
|
|
|
<div class="container" style="padding-top: 1.5rem; max-width: 1080px;">
|
|
<%- include('../partials/admin-header', { activeAdminTab: 'search', stats }) %>
|
|
|
|
<!-- Search Logs & Analytics -->
|
|
<section style="margin-bottom: 3rem;">
|
|
<div style="display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 1.25rem;">
|
|
<h2 style="font-size: 1.4rem; font-family: var(--font-serif); font-weight: 400; margin: 0;">Recent Search Queries (<%= searchLogs.length %>)</h2>
|
|
<span style="font-size: 0.85rem; color: var(--color-text-muted);">Recorded live in SQLite <code>search_logs</code></span>
|
|
</div>
|
|
|
|
<% if (searchLogs.length === 0) { %>
|
|
<p style="color: var(--color-text-muted); padding: 2rem; background: var(--color-surface); border-radius: var(--radius-sm); border: 1px dashed var(--color-border); text-align: center;">
|
|
No search queries recorded yet. Try searching on the <a href="/search" target="_blank" style="text-decoration: underline;">Search page</a>!
|
|
</p>
|
|
<% } else { %>
|
|
<div style="background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-sm); overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.02);">
|
|
<table style="width: 100%; border-collapse: collapse; font-size: 0.92rem; text-align: left;">
|
|
<thead>
|
|
<tr style="background: var(--color-surface-soft); border-bottom: 1px solid var(--color-border);">
|
|
<th style="padding: 0.85rem 1.25rem; font-weight: 600;">Query</th>
|
|
<th style="padding: 0.85rem 1rem; font-weight: 600;">Results Found</th>
|
|
<th style="padding: 0.85rem 1.25rem; font-weight: 600;">Searched At</th>
|
|
<th style="padding: 0.85rem 1rem; font-weight: 600; text-align: right;">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<% searchLogs.forEach(log => { %>
|
|
<tr style="border-bottom: 1px solid var(--color-border);">
|
|
<td style="padding: 0.85rem 1.25rem;">
|
|
<code style="background: var(--color-surface-soft); padding: 3px 8px; border-radius: 4px; font-family: var(--font-mono); font-size: 0.88rem; color: var(--color-text-main);"><%= log.query %></code>
|
|
</td>
|
|
<td style="padding: 0.85rem 1rem;">
|
|
<span class="pill <%= log.result_count > 0 ? 'pill-success' : 'pill-danger' %>" style="font-size: 0.8rem;">
|
|
<%= log.result_count %> <%= log.result_count === 1 ? 'result' : 'results' %>
|
|
</span>
|
|
</td>
|
|
<td style="padding: 0.85rem 1.25rem; color: var(--color-text-muted); font-size: 0.85rem;">
|
|
<%= new Date(log.searched_at).toLocaleString() %>
|
|
</td>
|
|
<td style="padding: 0.85rem 1rem; text-align: right;">
|
|
<a href="/search?q=<%= encodeURIComponent(log.query) %>" target="_blank" class="btn btn-secondary" style="padding: 0.25rem 0.65rem; font-size: 0.78rem;">
|
|
Run ↗
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<% }) %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<% } %>
|
|
</section>
|
|
|
|
<!-- Trending Search Recommendations -->
|
|
<section style="margin-bottom: 3rem;">
|
|
<h2 style="font-size: 1.4rem; font-family: var(--font-serif); font-weight: 400; margin-bottom: 1rem;">Active Search Recommendations</h2>
|
|
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap;">
|
|
<% (recommendations || []).forEach(rec => { %>
|
|
<div style="background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 0.6rem 1rem; display: flex; align-items: center; gap: 0.6rem;">
|
|
<code style="font-family: var(--font-mono); font-size: 0.85rem;"><%= rec.query %></code>
|
|
<span style="font-size: 0.78rem; color: var(--color-text-muted);">(<%= rec.clicks %> clicks)</span>
|
|
</div>
|
|
<% }) %>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<%- include('../partials/foot', { pageType: 'admin' }) %>
|