'use strict'; /* admin → «Генерация картинок»: провайдер Cloudflare (Account ID, токен, модель), * лимиты (пауза, дневной лимит), статистика и тест-генерация. */ (function () { 'use strict'; let inited = false; var esc = (window.LS && LS.escapeHtml) ? LS.escapeHtml : function (s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]; }); }; var IN = 'padding:8px 11px;border:1px solid var(--border,#e2e8f0);border-radius:9px;font:inherit;font-size:.85rem;width:100%;box-sizing:border-box;background:var(--surface,#fff);color:var(--text,#0f172a)'; function fmtBytes(n) { if (!n) return '0'; if (n >= 1048576) return (n / 1048576).toFixed(1) + ' МБ'; if (n >= 1024) return Math.round(n / 1024) + ' КБ'; return n + ' Б'; } function ensureStyle() { if (document.getElementById('img-adm-style')) return; var s = document.createElement('style'); s.id = 'img-adm-style'; s.textContent = [ '.img-card{border:1.5px solid var(--border,#e2e8f0);border-radius:14px;background:var(--surface,#fff);padding:16px 18px;margin-top:14px;}', '.img-flabel{font-size:.76rem;font-weight:700;color:var(--text-2,#475569);margin:11px 0 4px;}', '.img-row{display:flex;gap:12px;flex-wrap:wrap;}', '.img-row > div{flex:1;min-width:140px;}', '.img-bdg{font-size:.62rem;font-weight:800;text-transform:uppercase;letter-spacing:.03em;padding:3px 10px;border-radius:99px;}', '.img-bdg.on{background:rgba(5,150,82,.13);color:#059652;}', '.img-bdg.off{background:rgba(140,148,166,.16);color:#8a94a6;}', '.img-btn{padding:9px 17px;border-radius:10px;border:none;cursor:pointer;font:700 .82rem Manrope,sans-serif;}', '.img-btn.primary{background:#9B5DE5;color:#fff;}', '.img-btn.primary:hover{background:#7e3eca;}', '.img-btn.ghost{background:transparent;border:1.5px solid var(--border-h,#cbd5e1);color:var(--text-2,#475569);}', '.img-btn:disabled{opacity:.55;cursor:not-allowed;}', '.img-prev{margin-top:12px;border-radius:12px;overflow:hidden;border:1px solid var(--border,#e2e8f0);background:#0d0d1f;min-height:90px;display:flex;align-items:center;justify-content:center;}', '.img-prev img{max-width:100%;display:block;}', '.img-busy{color:#9aa5b4;font-size:.82rem;padding:24px;text-align:center;}', '.img-hint{font-size:.72rem;color:#8a94a6;margin-top:5px;line-height:1.45;}', ].join(''); document.head.appendChild(s); } async function render() { var host = document.getElementById('imggen-admin'); if (!host) return; ensureStyle(); host.innerHTML = '
Загрузка…
'; var cfg = {}; try { cfg = await LS.api('/api/admin/imggen'); } catch (e) { host.innerHTML = '
Не удалось загрузить настройки
'; return; } var models = cfg.models || []; var cdSec = Math.round((cfg.cooldownMs != null ? cfg.cooldownMs : 4000) / 1000); var on = cfg.on !== false; var badgeTxt = cfg.enabled ? 'Включена' : (cfg.configured ? 'Выключена' : 'Не настроена'); var badgeCls = cfg.enabled ? 'on' : 'off'; host.innerHTML = '
' + '
Генерация картинок включена
' + '
Главный выключатель. Выключено — генерация недоступна во всей системе (ассистент, флэшкарты, уроки, питомец, обложки, аватар, доска), даже если токен задан.
' + '' + '
' + '
' + '
' + '
Cloudflare Workers AI
' + '' + badgeTxt + '' + '
' + '
Бесплатная генерация (FLUX.1 / SDXL). Токен и Account ID — из дашборда Cloudflare (Workers AI). Хранятся в БД, не в git.
' + '
Account ID
' + '' + '
API-токен
' + '' + (cfg.hasToken ? '' : '') + '
Модель
' + '' + '
' + '
Пауза между запросами (сек)
' + '
Дневной лимит на пользователя
' + '
' + '
0 в дневном лимите — без ограничения.
' + '
' + '
Сгенерировано: ' + (cfg.stats ? cfg.stats.count : 0) + ' картинок · ' + (cfg.stats ? fmtBytes(cfg.stats.bytes) : '0') + '
' + '' + '
' + '
' + '
' + '
Тест генерации
' + '' + '
' + '' + '
'; var Q = function (s) { return host.querySelector(s); }; Q('#img-master').addEventListener('change', function () { var v = this.checked; LS.api('/api/admin/imggen', { method: 'PUT', body: JSON.stringify({ on: v }) }) .then(function () { LS.toast(v ? 'Генерация включена' : 'Генерация выключена для всех', 'success'); render(); }) .catch(function () { LS.toast('Ошибка', 'error'); render(); }); }); Q('#img-save').addEventListener('click', async function () { var btn = this; btn.disabled = true; var body = { provider: 'cloudflare', accountId: Q('#img-acc').value.trim(), model: Q('#img-model').value, cooldownMs: Math.max(0, Number(Q('#img-cd').value) || 0) * 1000, dailyCap: Math.max(0, Number(Q('#img-cap').value) || 0), }; var clr = Q('#img-clear-token'); if (clr && clr.checked) body.clearToken = true; else { var t = Q('#img-token').value.trim(); if (t) body.token = t; } try { await LS.api('/api/admin/imggen', { method: 'PUT', body: JSON.stringify(body) }); LS.toast('Сохранено', 'success'); render(); } catch (e) { LS.toast('Ошибка сохранения', 'error'); btn.disabled = false; } }); Q('#img-test-btn').addEventListener('click', async function () { var btn = this, prev = Q('#img-test-prev'); var prompt = Q('#img-test-prompt').value.trim(); btn.disabled = true; btn.textContent = 'Рисую…'; prev.style.display = 'flex'; prev.innerHTML = '
Генерирую… (5–15 сек)
'; try { var r = await LS.api('/api/admin/imggen/test', { method: 'POST', body: JSON.stringify({ prompt: prompt }) }); if (r && r.url) prev.innerHTML = ''; else prev.innerHTML = '
Пустой ответ
'; } catch (e) { var msg = (e && e.data && (e.data.error || e.data.detail)) || e.message || 'Ошибка'; prev.innerHTML = '
' + esc(msg) + '
'; } finally { btn.disabled = false; btn.textContent = 'Сгенерировать тест'; } }); } window.AdminSections = window.AdminSections || {}; window.AdminSections.imggen = { init: async () => { if (inited) return; inited = true; await render(); }, reload: render }; })();