'use client'; import { useState, useEffect } from 'react'; import { Loader2, Save, Eye, EyeOff, RefreshCw, Check, AlertCircle, BarChart3, Coins } from 'lucide-react'; import AdminBilling from './admin/AdminBilling'; const SETTING_LABELS = { AI_IMAGE_API_KEY: 'API ключ картинок', AI_IMAGE_BASE_URL: 'Base URL картинок', AI_IMAGE_FALLBACK_API_KEY: 'Fallback — API ключ', AI_IMAGE_FALLBACK_BASE_URL: 'Fallback — Base URL', AI_IMAGE_MODEL: 'Модель картинок', AI_IMAGE_MODEL_VIA_RESPONSES: 'Модель картинок (responses)', AI_PROVIDER_MARKUP: 'Наценка провайдера', AI_TEXT_API_KEY: 'API ключ текста', AI_TEXT_BASE_URL: 'Base URL провайдера', AI_TEXT_MODEL_ARTICLE: 'Модель для статей', AI_TEXT_MODEL_POST: 'Модель для постов', AI_TEXT_MODEL_TOPICS: 'Модель для тем', AI_USD_RUB_RATE: 'Курс USD → RUB', ROUTERAI_API_KEY: 'RouterAI — API ключ', ROUTERAI_BASE_URL: 'RouterAI — Base URL', ROUTERAI_IMAGE_MODEL: 'RouterAI — модель', APP_PUBLIC_URL: 'URL приложения', ENGINE_PUBLIC_URL: 'URL движка', TELEGRAM_API_BASE: 'Telegram Bot API', MAINTENANCE_MODE: 'Режим обслуживания', MAINTENANCE_MESSAGE: 'Сообщение при обслуживании', AUTO_DRAFT_DEFAULT_COUNT: 'Черновиков в день', AUTO_DRAFT_DEFAULT_TIME: 'Время генерации', DEFAULT_AI_STYLE_PROMPT: 'Стиль по умолчанию', DEFAULT_EMOJI_ENABLED: 'Эмодзи', DEFAULT_HASHTAGS_IN_POST: 'Хештеги в посте', DEFAULT_IMAGE_ENABLED: 'Генерировать картинку', DEFAULT_POST_GOAL: 'Цель поста', DEFAULT_POST_LANGUAGE: 'Язык', DEFAULT_POST_LENGTH: 'Длина поста', DEFAULT_POST_STYLE: 'Стиль', YUKASSA_SHOP_ID: 'ID магазина', YUKASSA_SECRET: 'Секретный ключ', YUKASSA_RETURN_URL: 'URL возврата', SMTP_ENABLED: 'Email включён', SMTP_FROM: 'Email отправителя', SMTP_HOST: 'SMTP сервер', SMTP_PASS: 'SMTP пароль', SMTP_PORT: 'SMTP порт', SMTP_USER: 'SMTP логин', PHOTO_SEARCH_PROVIDER: 'Провайдер поиска', YANDEX_SEARCH_API_KEY: 'Яндекс — API ключ', YANDEX_SEARCH_DAILY_LIMIT: 'Лимит запросов/день', YANDEX_SEARCH_FOLDER_ID: 'Яндекс — Folder ID', }; const TABS_SYS = [ { id: 'settings', label: 'Настройки API' }, { id: 'billing', label: 'Биллинг' }, ]; const CATEGORIES = [ { slug: 'ai_providers', title: 'AI — текст и картинки', hint: 'Ключи и модели для генерации текста и изображений. Изменения применяются без рестарта.' }, { slug: 'payments', title: 'Приём оплаты', hint: 'Настройки ЮKassa: ID магазина и секретный ключ из личного кабинета.' }, { slug: 'photo_search', title: 'Поиск фотографий', hint: 'Яндекс поиск: API ключ и лимиты на количество запросов в день.' }, { slug: 'engine', title: 'Движок и Telegram', hint: 'URL приложения, Telegram Bot API, режим обслуживания, настройки автогенерации.' }, { slug: 'email', title: 'Email уведомления', hint: 'SMTP настройки для отправки писем пользователям.' }, ]; export default function SystemSettings() { const [sysTab, setSysTab] = useState('settings'); const [byCategory, setByCategory] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); async function load() { setLoading(true); setError(''); try { const result = {}; for (const cat of CATEGORIES) { const res = await fetch(`/api/admin/settings?category=${cat.slug}`); if (!res.ok) throw new Error((await res.json()).error || `HTTP ${res.status}`); result[cat.slug] = await res.json(); } setByCategory(result); } catch (err) { setError(err.message); } finally { setLoading(false); } } useEffect(() => { load(); }, []); if (loading) { 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)} |
За выбранный период вызовов не было.
)} > )}{category.hint}
}Нет настроек в этой категории.
) : ({row.key}
{row.category} · обновлено{' '}
{row.updated_at ? new Date(row.updated_at).toLocaleString('ru-RU') : '—'}