fix(shorts): живые обновления страницы и лимит на вызовы ИИ

- shorts.js подписан на SSE shorts_changed с дебаунсом и реконнектом
- dispatchEvent больше не отдаёт любой неизвестный тип как
  entries_changed: entries_changed/lessons_changed/shorts_changed
  теперь маршрутизируются по имени (иначе правка короткого
  сообщения будила журнал с ложным тостом о новой записи)
- добавлен aiLimiter (30/мин на cache.rateLimitStore('ai')) и
  навешен на оба вызова модели: /api/ai/correct и
  /api/shorts/ai-correct
This commit is contained in:
dev
2026-10-06 00:09:28 +03:00
parent d00de2cb1a
commit 9f815e5143
2 changed files with 41 additions and 2 deletions
+15 -2
View File
@@ -159,6 +159,10 @@ function dispatchEvent(payload) {
});
return;
}
if (payload && (payload.type === 'entries_changed' || payload.type === 'lessons_changed' || payload.type === 'shorts_changed')) {
writeFrame(payload.type, { ts: Date.now() });
return;
}
if (payload && payload.type === 'ai_status') {
writeFrame('ai_status', {
id: payload.id,
@@ -1991,6 +1995,15 @@ const chatLimiter = rateLimit({
message: { error: 'Слишком много сообщений. Подождите минуту.' },
});
const aiLimiter = rateLimit({
windowMs: 60 * 1000,
max: 30,
standardHeaders: true,
legacyHeaders: false,
store: cache.rateLimitStore('ai', 60 * 1000),
message: { error: 'Слишком много запросов к ИИ. Подождите минуту.' },
});
async function chatThreadFor(user, threadId) {
const id = parseInt(threadId, 10);
if (!Number.isInteger(id) || id < 1) return { error: 400, message: 'Invalid thread' };
@@ -2747,7 +2760,7 @@ app.delete('/api/shorts/:id/permanent', requireAuth, requireAdmin, async (req, r
}
});
app.post('/api/shorts/ai-correct', requireAuth, async (req, res) => {
app.post('/api/shorts/ai-correct', requireAuth, aiLimiter, async (req, res) => {
try {
const text = reqStr(req.body?.text, 5000);
if (!text) return res.status(400).json({ error: 'Текст не указан' });
@@ -7486,7 +7499,7 @@ app.put('/api/entries/:id/unschedule', requireAuth, async (req, res) => {
res.json({ ok: true });
});
app.post('/api/ai/correct', requireAuth, async (req, res) => {
app.post('/api/ai/correct', requireAuth, aiLimiter, async (req, res) => {
const text = reqStr(req.body?.text, 5000);
if (!text) return res.status(400).json({ error: 'Текст не указан' });
try {