diff --git a/app/api/admin/usage/summary/route.js b/app/api/admin/usage/summary/route.js new file mode 100644 index 0000000..bf18558 --- /dev/null +++ b/app/api/admin/usage/summary/route.js @@ -0,0 +1,20 @@ +import { NextResponse } from 'next/server'; +import { requireAdmin } from '@/lib/session'; +import { engine } from '@/lib/engine'; + +// GET /api/admin/usage/summary?range=today&group_by=service +export async function GET(req) { + const admin = await requireAdmin(); + if (!admin) return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + try { + const { searchParams } = new URL(req.url); + const params = {}; + if (searchParams.get('range')) params.range = searchParams.get('range'); + if (searchParams.get('group_by')) params.group_by = searchParams.get('group_by'); + if (searchParams.get('service')) params.service = searchParams.get('service'); + const data = await engine.usageSummary(params); + return NextResponse.json(data); + } catch (err) { + return NextResponse.json({ error: err.message }, { status: err.status || 500 }); + } +} diff --git a/components/SystemSettings.js b/components/SystemSettings.js index ef2720f..7851091 100644 --- a/components/SystemSettings.js +++ b/components/SystemSettings.js @@ -1,13 +1,14 @@ 'use client'; import { useState, useEffect } from 'react'; -import { Loader2, Save, Eye, EyeOff, RefreshCw, Check, AlertCircle } from 'lucide-react'; +import { Loader2, Save, Eye, EyeOff, RefreshCw, Check, AlertCircle, BarChart3 } from 'lucide-react'; // Категории, которые управляются здесь (в админке tool, а не в админке блога). // Категория `engine` (TELEGRAM_API_BASE и т.п.) намеренно живёт в zeropost.ru/admin. const CATEGORIES = [ + { slug: 'ai_providers', title: 'AI провайдеры', + hint: 'Ключи, URL и модели для текстовой и картиночной генерации. Меняются на лету — рестарт engine не нужен. Курс USD↔РУБ и наценка реселлера тут же — влияют на расчёт стоимости в блоке «Расход AI» выше.' }, { slug: 'photo_search', title: 'Поиск фото', hint: 'Yandex Search API: provider, ключ, folder, лимиты.' }, - // Сюда позже: { slug: 'billing', ... }, { slug: 'serpapi', ... } ]; export default function SystemSettings() { @@ -59,6 +60,7 @@ export default function SystemSettings() { return (
| + {groupBy === 'service' ? 'Сервис' : groupBy === 'provider' ? 'Провайдер' : 'Модель'} + | +Вызовов | +Токены | +Картинки | +Стоимость | +
|---|---|---|---|---|
| {row.key} | +{fmtInt(row.calls)}{row.failed ? ({row.failed} err) : null} | +{fmtInt(row.prompt_tokens + row.completion_tokens)} | +{fmtInt(row.image_count)} | +{fmtRub(row.cost_rub)} | +
За выбранный период вызовов не было.
+ )} + > + )} +