feat: autogen blog admin API

routes/admin.js:
  GET  /autogen — настройки+статистика+очередь+размеры банков тем
  PATCH /autogen/:category — enabled/per_day/run_hour/run_minute
  POST  /autogen/:category/run — ручной запуск генерации
  POST  /autogen/queue — добавить тему с приоритетом
  DELETE /autogen/queue/:id — удалить тему
This commit is contained in:
Ник (Claude)
2026-06-13 10:35:50 +03:00
parent 6e1cd24b4e
commit b5fa77ea01
+85
View File
@@ -405,3 +405,88 @@ router.get('/logs', async (req, res) => {
res.json({ errors: all, total: all.length, topErrors }); res.json({ errors: all, total: all.length, topErrors });
} catch (err) { res.status(500).json({ error: err.message }); } } catch (err) { res.status(500).json({ error: err.message }); }
}); });
// ── AUTOGEN BLOG ─────────────────────────────────────────────
// GET /api/admin/autogen — статус автогенерации блога
router.get('/autogen', async (req, res) => {
if (!await requireAdmin(req, res)) return;
try {
const { getAutogenStatus, TOPIC_BANK } = require('../services/autogen');
const status = await getAutogenStatus();
// Статистика статей по категориям за последние 7 дней
const { rows: recentStats } = await query(`
SELECT category, count(*)::int as cnt_7d,
max(created_at) as last_article_at
FROM articles
WHERE status='published' AND created_at > NOW() - INTERVAL '7 days'
GROUP BY category
`);
const byCategory = Object.fromEntries(recentStats.map(r => [r.category, r]));
// Очередь тем
const { rows: queueItems } = await query(
`SELECT * FROM content_queue ORDER BY priority DESC, created_at ASC LIMIT 20`
);
const topicBankSizes = Object.fromEntries(
Object.entries(TOPIC_BANK).map(([k, v]) => [k, v.length])
);
res.json({ settings: status, byCategory, queue: queueItems, topicBankSizes });
} catch (err) { res.status(500).json({ error: err.message }); }
});
// PATCH /api/admin/autogen/:category — обновить настройки категории
router.patch('/autogen/:category', async (req, res) => {
if (!await requireAdmin(req, res)) return;
try {
const { enabled, per_day, run_hour, run_minute } = req.body;
const fields = []; const vals = []; let i = 1;
if (enabled !== undefined) { fields.push(`enabled=$${i++}`); vals.push(enabled); }
if (per_day !== undefined) { fields.push(`per_day=$${i++}`); vals.push(per_day); }
if (run_hour !== undefined) { fields.push(`run_hour=$${i++}`); vals.push(run_hour); }
if (run_minute !== undefined) { fields.push(`run_minute=$${i++}`); vals.push(run_minute); }
if (!fields.length) return res.status(400).json({ error: 'Nothing to update' });
vals.push(req.params.category);
await query(`UPDATE autogen_settings SET ${fields.join(',')} WHERE category=$${i}`, vals);
res.json({ ok: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});
// POST /api/admin/autogen/:category/run — запустить генерацию вручную
router.post('/autogen/:category/run', async (req, res) => {
if (!await requireAdmin(req, res)) return;
try {
res.json({ ok: true, message: `Генерация категории ${req.params.category} запущена` });
const { runAutogenForCategory } = require('../services/autogen');
runAutogenForCategory(req.params.category).catch(e =>
console.error(`[Autogen manual] ${req.params.category}: ${e.message}`)
);
} catch (err) { res.status(500).json({ error: err.message }); }
});
// POST /api/admin/autogen/queue — добавить тему в очередь
router.post('/autogen/queue', async (req, res) => {
if (!await requireAdmin(req, res)) return;
const { category, topic, tags = [], keywords = [], priority = 5 } = req.body;
if (!category || !topic) return res.status(400).json({ error: 'category и topic обязательны' });
try {
const { rows: [item] } = await query(
`INSERT INTO content_queue (category, topic, tags, keywords, priority)
VALUES ($1,$2,$3,$4,$5) RETURNING *`,
[category, topic, JSON.stringify(tags), JSON.stringify(keywords), priority]
);
res.json(item);
} catch (err) { res.status(500).json({ error: err.message }); }
});
// DELETE /api/admin/autogen/queue/:id
router.delete('/autogen/queue/:id', async (req, res) => {
if (!await requireAdmin(req, res)) return;
try {
await query('DELETE FROM content_queue WHERE id=$1', [req.params.id]);
res.json({ ok: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});