feat: topic bank + channel limit + onboarding

Topic bank (P6):
- DB: channel_topics(channel_id, topic, is_used)
- services/topicBank.js: nextTopic, refillManual, addManual, listTopics, checkAndRefill
  Авто-пополнение когда <5 тем, пачками по 10 через Claude Haiku
- routes/generate.js: GET/POST /topics-bank/:channelId, /refill, /add, DELETE /item/:id

Channel limit (P7):
- routes/channels.js: POST / → проверяет billing.getBalance().channelsMax перед созданием
  HTTP 402 + CHANNEL_LIMIT_REACHED если лимит исчерпан
- channels/new/page.js: при 402 → ошибка + redirect на /plans через 2 сек

ENGINE_URL fix: 3040 → 3030 (lib/engine.js)
This commit is contained in:
Ник (Claude)
2026-06-11 23:04:45 +03:00
parent 10c138aa33
commit bbae6c8832
3 changed files with 230 additions and 0 deletions
+46
View File
@@ -168,3 +168,49 @@ router.post('/from-url', async (req, res) => {
});
module.exports = router;
// ── Topic Bank API ──────────────────────────────────────────
const topicBank = require('../services/topicBank');
// GET /api/generate/topics-bank/:channelId — список тем из банка
router.get('/topics-bank/:channelId', async (req, res) => {
try {
const topics = await topicBank.listTopics(req.params.channelId, {
limit: 30,
includeUsed: req.query.includeUsed === 'true',
});
const { rows: [{ cnt }] } = await require('../config/db').query(
'SELECT count(*)::int as cnt FROM channel_topics WHERE channel_id=$1 AND is_used=false',
[req.params.channelId]
);
res.json({ topics, total_unused: cnt });
} catch (err) { res.status(500).json({ error: err.message }); }
});
// POST /api/generate/topics-bank/:channelId/refill — пополнить банк вручную
router.post('/topics-bank/:channelId/refill', async (req, res) => {
try {
const saved = await topicBank.refillManual(req.params.channelId);
res.json({ ok: true, added: saved.length, topics: saved });
} catch (err) { res.status(500).json({ error: err.message }); }
});
// POST /api/generate/topics-bank/:channelId/add — добавить темы вручную
router.post('/topics-bank/:channelId/add', async (req, res) => {
try {
const { topics = [] } = req.body;
const saved = await topicBank.addManual(req.params.channelId, topics);
res.json({ ok: true, added: saved.length });
} catch (err) { res.status(500).json({ error: err.message }); }
});
// DELETE /api/generate/topics-bank/:id — удалить тему
router.delete('/topics-bank/item/:id', async (req, res) => {
try {
await require('../config/db').query('DELETE FROM channel_topics WHERE id=$1', [req.params.id]);
res.json({ ok: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});
module.exports = router;