'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 ? '
очистить токен (выключить)' : '') +
'
Модель
' +
'
' +
models.map(function (m) { return '' + esc(m.label) + ' '; }).join('') +
' ' +
'
' +
'
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 };
})();