const state = { dashboard: null, }; async function api(path, options = {}) { const response = await fetch(path, { headers: { "Content-Type": "application/json" }, ...options, }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || "Request failed"); } return data; } function formatDate(value) { if (!value) return "—"; return value.replace("T", " ").replace("+00:00", " UTC"); } function setBusy(button, busy) { if (!button) return; button.disabled = busy; } function renderAuth(auth) { document.getElementById("auth-status").textContent = auth.status; document.getElementById("auth-details").textContent = auth.details || ""; } function renderSummary(data) { document.getElementById("channel-count").textContent = String(data.state.channel_count); document.getElementById("forwarding-count").textContent = String(data.state.forwarding_rules.length); const toggle = document.getElementById("scrape-media-toggle"); toggle.checked = Boolean(data.state.scrape_media); document.getElementById("scrape-media-label").textContent = toggle.checked ? "ON" : "OFF"; } function renderChannels(channels) { const tbody = document.getElementById("channels-table"); tbody.innerHTML = ""; const template = document.getElementById("channel-row-template"); channels.forEach((channel) => { const node = template.content.firstElementChild.cloneNode(true); node.querySelector(".channel-name").textContent = channel.name; node.querySelector(".channel-id").textContent = channel.channel_id; node.querySelector(".message-count").textContent = String(channel.message_count); node.querySelector(".media-count").textContent = String(channel.media_count); node.querySelector(".last-date").textContent = channel.last_date || "—"; node.querySelector(".scrape-btn").addEventListener("click", async () => { await api("/api/jobs/scrape", { method: "POST", body: JSON.stringify({ channel_id: channel.channel_id }), }); await refreshDashboard(); }); node.querySelector(".export-view-btn").addEventListener("click", () => { window.location.href = `/viewer?channel=${encodeURIComponent(channel.channel_id)}`; }); node.querySelector(".media-btn").addEventListener("click", async () => { await api("/api/jobs/rescrape-media", { method: "POST", body: JSON.stringify({ channel_id: channel.channel_id }), }); await refreshDashboard(); }); node.querySelector(".remove-btn").addEventListener("click", async () => { await api("/api/channels/remove", { method: "POST", body: JSON.stringify({ channel_id: channel.channel_id }), }); await refreshDashboard(); }); tbody.appendChild(node); }); } function renderJobs(jobs) { const root = document.getElementById("jobs-list"); root.innerHTML = ""; const template = document.getElementById("job-template"); if (!jobs.length) { root.innerHTML = '
Пока задач нет.
'; return; } jobs.forEach((job) => { const node = template.content.firstElementChild.cloneNode(true); node.querySelector(".job-title").textContent = job.title; node.querySelector(".job-status").textContent = job.status; node.querySelector(".job-time").textContent = [job.started_at, job.finished_at].filter(Boolean).join(" -> "); node.querySelector(".job-logs").textContent = job.logs || job.error || "Без логов"; root.appendChild(node); }); } async function refreshDashboard() { const data = await api("/api/dashboard"); state.dashboard = data; renderAuth(data.auth); renderSummary(data); renderChannels(data.channels); renderJobs(data.jobs); } async function main() { document.getElementById("scrape-all-btn").addEventListener("click", async (event) => { setBusy(event.currentTarget, true); try { await api("/api/jobs/scrape", { method: "POST", body: JSON.stringify({}) }); await refreshDashboard(); } finally { setBusy(event.currentTarget, false); } }); document.getElementById("export-all-btn").addEventListener("click", async (event) => { setBusy(event.currentTarget, true); try { await api("/api/jobs/export", { method: "POST", body: JSON.stringify({}) }); await refreshDashboard(); } finally { setBusy(event.currentTarget, false); } }); document.getElementById("refresh-dialogs-btn").addEventListener("click", async (event) => { setBusy(event.currentTarget, true); try { await api("/api/jobs/refresh-dialogs", { method: "POST", body: JSON.stringify({}) }); await refreshDashboard(); } finally { setBusy(event.currentTarget, false); } }); document.getElementById("refresh-jobs-btn").addEventListener("click", refreshDashboard); document.getElementById("scrape-media-toggle").addEventListener("change", async (event) => { const checked = event.currentTarget.checked; document.getElementById("scrape-media-label").textContent = checked ? "ON" : "OFF"; await api("/api/settings/media", { method: "POST", body: JSON.stringify({ value: checked }), }); await refreshDashboard(); }); document.getElementById("add-channel-form").addEventListener("submit", async (event) => { event.preventDefault(); const channelId = document.getElementById("add-channel-id").value.trim(); const name = document.getElementById("add-channel-name").value.trim(); if (!channelId) return; await api("/api/channels/add", { method: "POST", body: JSON.stringify({ channel_id: channelId, name }), }); event.currentTarget.reset(); await refreshDashboard(); }); await refreshDashboard(); window.setInterval(refreshDashboard, 5000); } main().catch((error) => { console.error(error); alert(error.message); });