TTW_Bot_GO/internal/webui/templates/webservices.html

251 lines
14 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{define "content"}}
<h2>Веб-сервисы для OBS</h2>
<p>Создавайте несколько независимых оверлеев чата и оповещений. Каждый сервис работает на своём порту.</p>
<div class="card">
<button id="add-chat-btn" style="margin-right:10px;"> Добавить чат-оверлей</button>
<button id="add-alert-btn">🔔 Добавить оверлей оповещений</button>
</div>
<div class="card">
<h3>Список сервисов</h3>
<table id="services-table" style="width:100%; border-collapse: collapse;">
<thead>
<tr><th>ID</th><th>Тип</th><th>Порт</th><th>Статус</th><th>Действия</th></tr>
</thead>
<tbody>
<tr><td colspan="5">Загрузка...</td></tr>
</tbody>
</table>
</div>
<!-- Модальное окно для создания/редактирования -->
<div id="service-modal" class="modal">
<div class="modal-content">
<h3 id="modal-title">Создание сервиса</h3>
<form id="service-form">
<input type="hidden" id="service-id" value="0">
<label>Порт: <input type="number" id="service-port" required style="width:100%;"></label>
<label><input type="checkbox" id="service-enabled" checked> Сервис активен</label>
<!-- Настройки чата (показываются только для типа chat) -->
<div id="chat-config-fields" style="display:none;">
<h4>Настройки чата</h4>
<label>Фон (цвет): <input type="color" id="chat-bg-color" value="#000000"></label>
<label>Цвет текста: <input type="color" id="chat-text-color" value="#ffffff"></label>
<label>Размер шрифта (px): <input type="number" id="chat-font-size" min="10" max="50" value="14"></label>
<label>Шрифт: <input type="text" id="chat-font-family" value="Arial, sans-serif"></label>
<label>Прозрачность (%): <input type="range" id="chat-opacity" min="0" max="100" value="80"> <span id="opacity-val">80</span></label>
<label>Таймаут сообщения (сек): <input type="number" id="chat-timeout" min="0" max="60" value="10"></label>
<label>Макс. сообщений: <input type="number" id="chat-max-msgs" min="1" max="100" value="20"></label>
<label><input type="checkbox" id="chat-show-badges" checked> Показывать значки</label>
<label><input type="checkbox" id="chat-show-timestamps"> Показывать время</label>
<div class="preview-box">
<strong>Превью:</strong>
<div id="chat-preview" class="chat-preview" style="background:#000; color:#fff; padding:8px; border-radius:4px; margin-top:6px;">
<span style="font-weight:bold;">TestUser:</span> Привет, мир!
</div>
</div>
</div>
<!-- Для alert-сервиса ничего лишнего не показываем -->
<div id="alert-config-fields" style="display:none;">
<p class="info">Оповещения настраиваются через HTTP-запросы к эндпоинту <code>/notify</code> этого сервиса. Все уведомления будут отображаться автоматически.</p>
</div>
<div style="margin-top:15px;">
<button type="submit">Сохранить</button>
<button type="button" id="close-modal">Отмена</button>
</div>
</form>
</div>
</div>
<script>
let currentType = 'chat';
let services = [];
async function loadServices() {
try {
const res = await fetch('/api/webservices');
if (!res.ok) throw new Error('HTTP ' + res.status);
services = await res.json();
if (!Array.isArray(services)) services = [];
const tbody = document.querySelector('#services-table tbody');
if (!services.length) {
tbody.innerHTML = '<tr><td colspan="5">Нет сервисов. Создайте первый.</td></tr>';
return;
}
tbody.innerHTML = services.map(s => `
<tr>
<td>${s.id}</td>
<td>${s.service_type === 'chat' ? 'Чат' : 'Оповещения'}</td>
<td>${s.port}</td>
<td class="status-${s.id}">${s.running ? '✅ Запущен' : (s.enabled ? '⏹ Остановлен' : '❌ Отключен')}</td>
<td>
<button onclick="startService(${s.id})">▶ Запустить</button>
<button onclick="stopService(${s.id})">⏹ Остановить</button>
<button onclick="editService(${s.id})">✏️ Редактировать</button>
<button onclick="deleteService(${s.id})">🗑 Удалить</button>
</td>
</tr>
`).join('');
} catch (err) {
console.error('loadServices error:', err);
document.querySelector('#services-table tbody').innerHTML = '<tr><td colspan="5">Ошибка загрузки</td></tr>';
}
}
async function startService(id) {
const res = await fetch(`/api/webservices/start?id=${id}`, { method: 'POST' });
if (res.ok) loadServices(); else alert('Ошибка запуска');
}
async function stopService(id) {
const res = await fetch(`/api/webservices/stop?id=${id}`, { method: 'POST' });
if (res.ok) loadServices(); else alert('Ошибка остановки');
}
async function deleteService(id) {
if (!confirm('Удалить сервис?')) return;
const res = await fetch(`/api/webservices/delete?id=${id}`, { method: 'DELETE' });
if (res.ok) loadServices(); else alert('Ошибка удаления');
}
async function editService(id) {
const service = services.find(s => s.id === id);
if (!service) return;
currentType = service.service_type;
document.getElementById('service-id').value = service.id;
document.getElementById('service-port').value = service.port;
document.getElementById('service-enabled').checked = service.enabled;
if (currentType === 'chat') {
const cfg = service.config_json ? JSON.parse(service.config_json) : {};
document.getElementById('chat-bg-color').value = cfg.bg_color || '#000000';
document.getElementById('chat-text-color').value = cfg.text_color || '#ffffff';
document.getElementById('chat-font-size').value = cfg.font_size || 14;
document.getElementById('chat-font-family').value = cfg.font_family || 'Arial, sans-serif';
document.getElementById('chat-opacity').value = cfg.opacity || 80;
document.getElementById('opacity-val').innerText = cfg.opacity || 80;
document.getElementById('chat-timeout').value = cfg.message_timeout_sec || 10;
document.getElementById('chat-max-msgs').value = cfg.max_messages || 20;
document.getElementById('chat-show-badges').checked = cfg.show_badges !== undefined ? cfg.show_badges : true;
document.getElementById('chat-show-timestamps').checked = cfg.show_timestamps || false;
updateChatPreview();
}
showModal();
}
function updateChatPreview() {
const bg = document.getElementById('chat-bg-color').value;
const color = document.getElementById('chat-text-color').value;
const fontSize = document.getElementById('chat-font-size').value;
const fontFamily = document.getElementById('chat-font-family').value;
const opacity = document.getElementById('chat-opacity').value;
const showBadges = document.getElementById('chat-show-badges').checked;
const showTimestamps = document.getElementById('chat-show-timestamps').checked;
const previewDiv = document.getElementById('chat-preview');
previewDiv.style.backgroundColor = bg;
previewDiv.style.color = color;
previewDiv.style.fontSize = fontSize + 'px';
previewDiv.style.fontFamily = fontFamily;
previewDiv.style.opacity = opacity/100;
let badgesHtml = '';
if (showBadges) badgesHtml = '<span style="margin-right:4px;">🎭</span><span style="margin-right:4px;">👑</span><span>✔️</span> ';
let timeHtml = '';
if (showTimestamps) timeHtml = '<span style="color:#aaa; margin-right:8px;">12:34</span> ';
previewDiv.innerHTML = timeHtml + badgesHtml + '<span style="font-weight:bold;">TestUser:</span> Привет, мир!';
}
function showModal() {
document.getElementById('service-modal').style.display = 'flex';
document.getElementById('chat-config-fields').style.display = currentType === 'chat' ? 'block' : 'none';
document.getElementById('alert-config-fields').style.display = currentType === 'alert' ? 'block' : 'none';
document.getElementById('modal-title').innerText = (document.getElementById('service-id').value == 0 ? 'Создание' : 'Редактирование') + (currentType === 'chat' ? ' чат-оверлея' : ' оверлея оповещений');
}
async function openCreateDialog(type) {
currentType = type;
document.getElementById('service-id').value = 0;
document.getElementById('service-enabled').checked = true;
// Определяем свободный порт
const res = await fetch('/api/webservices');
let servicesList = [];
if (res.ok) {
servicesList = await res.json();
if (!Array.isArray(servicesList)) servicesList = [];
}
const usedPorts = servicesList.map(s => s.port);
let basePort = 9000;
while (usedPorts.includes(basePort)) basePort++;
document.getElementById('service-port').value = basePort;
if (type === 'chat') {
// Настройки чата по умолчанию
document.getElementById('chat-bg-color').value = '#000000';
document.getElementById('chat-text-color').value = '#ffffff';
document.getElementById('chat-font-size').value = 14;
document.getElementById('chat-font-family').value = 'Arial, sans-serif';
document.getElementById('chat-opacity').value = 80;
document.getElementById('opacity-val').innerText = '80';
document.getElementById('chat-timeout').value = 10;
document.getElementById('chat-max-msgs').value = 20;
document.getElementById('chat-show-badges').checked = true;
document.getElementById('chat-show-timestamps').checked = false;
updateChatPreview();
}
showModal();
}
document.getElementById('add-chat-btn').onclick = () => openCreateDialog('chat');
document.getElementById('add-alert-btn').onclick = () => openCreateDialog('alert');
document.getElementById('close-modal').onclick = () => document.getElementById('service-modal').style.display = 'none';
// Обработчики для превью чата
document.getElementById('chat-bg-color').addEventListener('input', updateChatPreview);
document.getElementById('chat-text-color').addEventListener('input', updateChatPreview);
document.getElementById('chat-font-size').addEventListener('input', updateChatPreview);
document.getElementById('chat-font-family').addEventListener('input', updateChatPreview);
document.getElementById('chat-opacity').addEventListener('input', (e) => { document.getElementById('opacity-val').innerText = e.target.value; updateChatPreview(); });
document.getElementById('chat-show-badges').addEventListener('change', updateChatPreview);
document.getElementById('chat-show-timestamps').addEventListener('change', updateChatPreview);
document.getElementById('service-form').onsubmit = async (e) => {
e.preventDefault();
const id = parseInt(document.getElementById('service-id').value);
const port = parseInt(document.getElementById('service-port').value);
const enabled = document.getElementById('service-enabled').checked;
let config = {};
if (currentType === 'chat') {
config = {
bg_color: document.getElementById('chat-bg-color').value,
text_color: document.getElementById('chat-text-color').value,
font_size: parseInt(document.getElementById('chat-font-size').value),
font_family: document.getElementById('chat-font-family').value,
opacity: parseInt(document.getElementById('chat-opacity').value),
message_timeout_sec: parseInt(document.getElementById('chat-timeout').value),
max_messages: parseInt(document.getElementById('chat-max-msgs').value),
show_badges: document.getElementById('chat-show-badges').checked,
show_timestamps: document.getElementById('chat-show-timestamps').checked
};
} else {
// Для alert-сервиса отправляем пустой конфиг
config = {};
}
const url = id === 0 ? '/api/webservices/create' : `/api/webservices/update?id=${id}`;
const method = id === 0 ? 'POST' : 'PUT';
const body = JSON.stringify({ type: currentType, port, config, enabled });
const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body });
if (res.ok) {
document.getElementById('service-modal').style.display = 'none';
loadServices();
} else {
const err = await res.text();
alert('Ошибка: ' + err);
}
};
loadServices();
setInterval(loadServices, 5000);
</script>
{{end}}