feat(blog-topics): PATCH endpoint + soften requireAdmin + dynamic category name
- requireAdmin softened: doesn't require x-user-id when users.is_admin column is absent (same pattern as /api/admin/zero). Auth still enforced via the global x-internal-secret middleware. - /api/admin/blog-topics/:id now supports PATCH (topic/tags/priority/is_used). - /api/admin/blog-topics/generate reads category name from categories table instead of using a hardcoded 4-entry map, so AI-generation works for any user-created category.
This commit is contained in:
+42
-9
@@ -9,11 +9,19 @@ const { query } = require('../config/db');
|
||||
function uid(req) { return req.headers['x-user-id'] ? parseInt(req.headers['x-user-id']) : null; }
|
||||
|
||||
async function requireAdmin(req, res) {
|
||||
// x-internal-secret уже проверен глобальным middleware (см. index.js).
|
||||
// Опционально — если есть users.is_admin (multi-user окружение, как на dev2) — проверим;
|
||||
// если колонки нет (минимальный prod), доверяем секрету.
|
||||
const adminId = uid(req);
|
||||
if (!adminId) { res.status(401).json({ error: 'x-user-id required' }); return null; }
|
||||
if (!adminId) return 'system';
|
||||
try {
|
||||
const { rows: [u] } = await query('SELECT is_admin FROM users WHERE id=$1', [adminId]);
|
||||
if (!u?.is_admin) { res.status(403).json({ error: 'Forbidden' }); return null; }
|
||||
if (u && u.is_admin === false) { res.status(403).json({ error: 'Forbidden' }); return null; }
|
||||
return adminId;
|
||||
} catch (err) {
|
||||
// колонки is_admin нет — это нормально для prod конфига
|
||||
return adminId;
|
||||
}
|
||||
}
|
||||
|
||||
// GET /api/admin/dashboard
|
||||
@@ -559,6 +567,29 @@ router.post('/blog-topics', async (req, res) => {
|
||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||
});
|
||||
|
||||
|
||||
// PATCH /api/admin/blog-topics/:id — обновить тему (topic, tags, priority, is_used)
|
||||
router.patch('/blog-topics/:id', async (req, res) => {
|
||||
if (!await requireAdmin(req, res)) return;
|
||||
const { topic, tags, priority, is_used } = req.body || {};
|
||||
const fields = [];
|
||||
const vals = [];
|
||||
if (topic !== undefined) { fields.push(`topic=$${fields.length+1}`); vals.push(String(topic).trim()); }
|
||||
if (tags !== undefined) { fields.push(`tags=$${fields.length+1}`); vals.push(Array.isArray(tags) ? tags : []); }
|
||||
if (priority !== undefined) { fields.push(`priority=$${fields.length+1}`); vals.push(parseInt(priority, 10) || 5); }
|
||||
if (is_used !== undefined) { fields.push(`is_used=$${fields.length+1}`); vals.push(!!is_used); }
|
||||
if (!fields.length) return res.status(400).json({ error: 'нечего обновлять' });
|
||||
vals.push(parseInt(req.params.id, 10));
|
||||
try {
|
||||
const { rows: [row] } = await query(
|
||||
`UPDATE blog_topics SET ${fields.join(', ')} WHERE id=$${vals.length} RETURNING *`,
|
||||
vals
|
||||
);
|
||||
if (!row) return res.status(404).json({ error: 'не найдено' });
|
||||
res.json({ ok: true, topic: row });
|
||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||
});
|
||||
|
||||
// DELETE /api/admin/blog-topics/:id
|
||||
router.delete('/blog-topics/:id', async (req, res) => {
|
||||
if (!await requireAdmin(req, res)) return;
|
||||
@@ -581,14 +612,16 @@ router.post('/blog-topics/generate', async (req, res) => {
|
||||
|
||||
const ai = require('../services/ai');
|
||||
const config = require('../config');
|
||||
const CATEGORY_NAMES = {
|
||||
'ai-tools': 'AI инструменты для работы и бизнеса',
|
||||
'ai-dev': 'AI разработка и программирование',
|
||||
'automation': 'Автоматизация процессов',
|
||||
'cybersec': 'Кибербезопасность',
|
||||
};
|
||||
|
||||
const system = `Ты редактор tech-блога. Генерируй темы для статей категории "${CATEGORY_NAMES[category] || category}".
|
||||
// Имя и описание категории берём из БД (а не hardcoded)
|
||||
const { rows: [catRow] } = await query(
|
||||
'SELECT name, description FROM categories WHERE slug=$1',
|
||||
[category]
|
||||
);
|
||||
const catName = catRow?.name || category;
|
||||
const catDescr = catRow?.description ? ` (${catRow.description})` : '';
|
||||
|
||||
const system = `Ты редактор tech-блога. Генерируй темы для статей категории "${catName}"${catDescr}.
|
||||
Темы должны быть: конкретными, практическими, интересными читателям.
|
||||
Формат: точные заголовки статей, не категории.
|
||||
Ответь ТОЛЬКО JSON-массивом строк без markdown.`;
|
||||
|
||||
Reference in New Issue
Block a user