feat(admin): add ephemeral staging DB, read-only posts overview, and dynamic search

- Implement ephemeral staging mode (APP_ENV=staging) to isolate test/dev DB writes in /tmp
- Add read-only posts management dashboard under /guanli/posts with live links and comment counters
- Add hot re-indexing endpoint (/guanli/reindex) to refresh Markdown without restarting server
- Replace static recommended search config with dynamic frequency-based trending searches
- Remove early scaffold mockup file (dropbox/zh.md) and clean legacy mock test queries
- Add native Makefile with dev, dev-persistent, test, lint, and docker targets
- Update test suite and documentation for development workflows
This commit is contained in:
2026-09-16 23:09:14 -07:00
parent f52b3fe9e4
commit 95a7dcb93b
12 changed files with 510 additions and 83 deletions
+51
View File
@@ -102,6 +102,57 @@ router.get('/guanli/logout', (req, res) => {
const dbService = require('../services/db');
// 0. Posts Read-Only Overview Dashboard
router.get('/guanli/posts', requireAdminAuth, (req, res) => {
const q = (req.query.q || '').trim().toLowerCase();
const activeCategory = (req.query.category || '').trim();
const allPosts = contentService.getAllPosts();
let filteredPosts = allPosts;
if (activeCategory) {
filteredPosts = filteredPosts.filter(p => (p.category || '').toLowerCase() === activeCategory.toLowerCase());
}
if (q) {
filteredPosts = filteredPosts.filter(p =>
(p.title || '').toLowerCase().includes(q) ||
(p.slug || '').toLowerCase().includes(q) ||
(p.tags || []).some(t => t.toLowerCase().includes(q))
);
}
const taxonomy = contentService.getTaxonomy();
const commentStats = dbService.getPostCommentStats();
const stats = dbService.getAdminStats();
res.render('pages/admin-posts', {
title: 'Posts Overview - Site Administration',
posts: allPosts,
filteredPosts,
categories: taxonomy.categories,
activeCategory,
q: req.query.q || '',
commentStats,
stats
});
});
// Hot Content Re-index & Cache Flush endpoint
router.post('/guanli/reindex', requireAdminAuth, (req, res) => {
try {
const { CONTENT_DIR } = require('../config');
contentService.init(CONTENT_DIR);
pageCache.clear();
const totalPosts = contentService.getAllPosts().length;
const totalPages = contentService.pagesBySlug.size;
res.json({ success: true, totalPosts, totalPages });
} catch (err) {
console.error('[Admin] Hot re-index failed:', err);
res.status(500).json({ success: false, error: err.message });
}
});
// 1. Comment Moderation Dashboard
router.get('/guanli/comments', requireAdminAuth, (req, res) => {
const status = req.query.status || 'all';