feat: AdminQueue — generation queue UI

AdminQueue.js: статистика по статусам + список 30 последних задач
  4 счётчика (done/processing/pending/failed) с цветами
  Алерт для застрявших задач + кнопка Сбросить
  Фильтр по статусу, retry для failed задач
  Детали: тип, тема, ошибка, токены, время
AdminPanel: раздел Очередь между Движком и Тарифами
API routes: /api/admin/queue (GET+DELETE), /api/admin/queue/[id]/retry
This commit is contained in:
Ник (Claude)
2026-06-13 10:14:10 +03:00
parent b620927c25
commit 92872ed59c
4 changed files with 222 additions and 2 deletions
+15
View File
@@ -0,0 +1,15 @@
import { NextResponse } from 'next/server';
import { requireUser } from '@/lib/session';
const ENGINE_URL = process.env.ENGINE_URL || 'http://127.0.0.1:3030';
const ENGINE_SECRET = process.env.ENGINE_SECRET || '';
export async function POST(req, { params }) {
const user = await requireUser();
if (!user?.isAdmin) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
const res = await fetch(`${ENGINE_URL}/api/admin/queue/${params.id}/retry`, {
method: 'POST',
headers: { 'x-internal-secret': ENGINE_SECRET, 'x-user-id': String(user.id) },
});
return NextResponse.json(await res.json());
}
+20
View File
@@ -0,0 +1,20 @@
import { NextResponse } from 'next/server';
import { requireUser } from '@/lib/session';
const ENGINE_URL = process.env.ENGINE_URL || 'http://127.0.0.1:3030';
const ENGINE_SECRET = process.env.ENGINE_SECRET || '';
const h = (uid) => ({ 'x-internal-secret': ENGINE_SECRET, 'x-user-id': String(uid) });
export async function GET() {
const user = await requireUser();
if (!user?.isAdmin) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
const res = await fetch(`${ENGINE_URL}/api/admin/queue`, { headers: h(user.id), cache: 'no-store' });
return NextResponse.json(await res.json());
}
export async function DELETE() {
const user = await requireUser();
if (!user?.isAdmin) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
const res = await fetch(`${ENGINE_URL}/api/admin/queue/stuck`, { method: 'DELETE', headers: h(user.id) });
return NextResponse.json(await res.json());
}