(function () { const statusLabels = { pending: "排队中", running: "执行中", cancel_requested: "停止中", completed: "已完成", stopped: "已停止", failed: "失败", idle: "未启动" }; function renderLogLine(log) { const line = document.createElement("div"); const level = (log.level || "INFO").toLowerCase(); line.className = `log-line level-${level}`; const meta = document.createElement("span"); meta.className = "log-meta"; const owner = log.student_id || "system"; meta.textContent = `${log.created_at} · ${owner} · ${log.scope} · ${log.level}`; const message = document.createElement("span"); message.textContent = log.message || ""; line.append(meta, message); return line; } function bindLogStream() { const shell = document.querySelector("[data-log-stream-url]"); const consoleNode = document.getElementById("log-console"); if (!shell || !consoleNode || !window.EventSource) { return; } const streamUrl = shell.getAttribute("data-log-stream-url"); const eventSource = new EventSource(streamUrl); eventSource.onmessage = function (event) { try { const payload = JSON.parse(event.data); if (consoleNode.querySelector(".muted")) { consoleNode.innerHTML = ""; } consoleNode.appendChild(renderLogLine(payload)); while (consoleNode.children.length > 360) { consoleNode.removeChild(consoleNode.firstChild); } consoleNode.scrollTop = consoleNode.scrollHeight; } catch (_error) { // Ignore malformed frames and keep the stream alive. } }; } function updateUserStatus(data) { const taskText = document.getElementById("task-status-text"); const taskPill = document.getElementById("task-status-pill"); const courseCount = document.getElementById("course-count"); const status = (data.task && data.task.status) || "idle"; if (taskText) { taskText.textContent = statusLabels[status] || status; } if (taskPill) { taskPill.className = `status-pill status-${status}`; taskPill.textContent = statusLabels[status] || status; } if (courseCount && Array.isArray(data.courses)) { courseCount.textContent = String(data.courses.length); } } function updateAdminStatus(data) { const users = document.getElementById("stat-users"); const running = document.getElementById("stat-running"); const pending = document.getElementById("stat-pending"); const parallel = document.getElementById("parallel-limit-input"); if (users) { users.textContent = String(data.stats.users_count || 0); } if (running) { running.textContent = String(data.stats.running_count || 0); } if (pending) { pending.textContent = String(data.stats.pending_count || 0); } if (parallel) { parallel.value = String(data.parallel_limit || parallel.value); } } function bindStatusPolling() { const shell = document.querySelector("[data-status-url]"); if (!shell) { return; } const statusUrl = shell.getAttribute("data-status-url"); const isAdmin = shell.classList.contains("admin-dashboard"); async function refresh() { try { const response = await fetch(statusUrl, { headers: { "X-Requested-With": "fetch" } }); if (!response.ok) { return; } const payload = await response.json(); if (!payload.ok) { return; } if (isAdmin) { updateAdminStatus(payload); } else { updateUserStatus(payload); } } catch (_error) { // Leave the current UI state as-is if polling fails. } } refresh(); window.setInterval(refresh, 7000); } bindLogStream(); bindStatusPolling(); })();