233 lines
8.4 KiB
JavaScript
233 lines
8.4 KiB
JavaScript
|
|
const SCRIPT = "/home/ztl/LJ360/bash/video_tool.sh";
|
|||
|
|
const SERVICE = "lj360_camera";
|
|||
|
|
const LOG_FILE = "/home/ztl/LJ360/lj360_camera_keepalive.log";
|
|||
|
|
const WEBPY = "/home/ztl/LJ360/web.py";
|
|||
|
|
|
|||
|
|
// DOM 元素
|
|||
|
|
let statusIcon, statusText, outputBox, autostartSwitch;
|
|||
|
|
|
|||
|
|
function initElements() {
|
|||
|
|
statusIcon = document.getElementById("status-icon");
|
|||
|
|
statusText = document.getElementById("status-text");
|
|||
|
|
outputBox = document.getElementById("output");
|
|||
|
|
autostartSwitch = document.getElementById("autostart-switch");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setOutput(text) {
|
|||
|
|
if (outputBox) {
|
|||
|
|
outputBox.textContent = text;
|
|||
|
|
outputBox.scrollTop = outputBox.scrollHeight;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setStatus(text, isActive) {
|
|||
|
|
if (statusText) {
|
|||
|
|
statusText.textContent = text;
|
|||
|
|
}
|
|||
|
|
const panel = document.getElementById("status-panel");
|
|||
|
|
if (panel) {
|
|||
|
|
// 移除现有状态类
|
|||
|
|
panel.classList.remove("status-success", "status-danger", "status-warning");
|
|||
|
|
if (isActive === true) {
|
|||
|
|
panel.classList.add("status-success");
|
|||
|
|
} else if (isActive === false) {
|
|||
|
|
panel.classList.add("status-danger");
|
|||
|
|
} else if (isActive === undefined) {
|
|||
|
|
panel.classList.add("status-warning");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (statusIcon) {
|
|||
|
|
if (isActive === true) {
|
|||
|
|
statusIcon.innerHTML = '<i class="fa fa-check-circle" style="color:#3f9c35"></i>';
|
|||
|
|
} else if (isActive === false) {
|
|||
|
|
statusIcon.innerHTML = '<i class="fa fa-exclamation-circle" style="color:#c00"></i>';
|
|||
|
|
} else {
|
|||
|
|
statusIcon.innerHTML = '<i class="fa fa-refresh fa-spin"></i>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function runCommand(args, callback) {
|
|||
|
|
const proc = cockpit.spawn(args, { superuser: "require", err: "out" });
|
|||
|
|
let output = "";
|
|||
|
|
proc.stream(data => { output += data; });
|
|||
|
|
proc.then(() => callback(output, null));
|
|||
|
|
proc.catch(err => callback(output, err));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取服务状态和开机自启状态
|
|||
|
|
function refreshStatus() {
|
|||
|
|
setStatus("正在获取服务状态...", undefined);
|
|||
|
|
setOutput("⏳ 正在刷新状态...");
|
|||
|
|
|
|||
|
|
// 获取服务运行状态
|
|||
|
|
runCommand(["systemctl", "is-active", SERVICE + ".service"], (outActive, errActive) => {
|
|||
|
|
const isRunning = (outActive.trim() === "active");
|
|||
|
|
|
|||
|
|
// 获取开机自启状态
|
|||
|
|
runCommand(["systemctl", "is-enabled", SERVICE + ".service"], (outEnabled, errEnabled) => {
|
|||
|
|
let isEnabled = false;
|
|||
|
|
if (!errEnabled) {
|
|||
|
|
const enabledOut = outEnabled.trim();
|
|||
|
|
isEnabled = (enabledOut === "enabled" || enabledOut === "static");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新开关状态(不触发change事件)
|
|||
|
|
if (autostartSwitch) {
|
|||
|
|
autostartSwitch.checked = isEnabled;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新状态文本
|
|||
|
|
if (isRunning) {
|
|||
|
|
setStatus("服务正在运行" + (isEnabled ? " (开机自启已启用)" : " (开机自启未启用)"), true);
|
|||
|
|
} else {
|
|||
|
|
setStatus("服务未运行" + (isEnabled ? " (开机自启已启用但未运行)" : " (开机自启未启用)"), false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取详细状态输出
|
|||
|
|
runCommand(["systemctl", "status", SERVICE + ".service", "--no-pager", "-l"], (outDetail) => {
|
|||
|
|
setOutput(outDetail || "无状态信息");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置开机自启(通过开关)
|
|||
|
|
function setAutostart(enabled) {
|
|||
|
|
setOutput("⏳ " + (enabled ? "正在启用开机自启..." : "正在禁用开机自启..."));
|
|||
|
|
const action = enabled ? "enable_autostart" : "disable_autostart";
|
|||
|
|
runCommand(["bash", SCRIPT, action], (out, err) => {
|
|||
|
|
if (err) {
|
|||
|
|
setOutput("❌ 错误: " + (err.message || err) + "\n" + (out || ""));
|
|||
|
|
// 恢复开关状态
|
|||
|
|
setTimeout(() => refreshStatus(), 500);
|
|||
|
|
} else {
|
|||
|
|
setOutput((enabled ? "✅ 开机自启已启用" : "✅ 开机自启已禁用") + "\n" + (out || ""));
|
|||
|
|
setTimeout(() => refreshStatus(), 300);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动服务
|
|||
|
|
function startService() {
|
|||
|
|
setOutput("⏳ 正在启动服务...");
|
|||
|
|
runCommand(["systemctl", "start", SERVICE + ".service"], (out, err) => {
|
|||
|
|
if (err) {
|
|||
|
|
setOutput("❌ 启动失败: " + (err.message || err) + "\n" + out);
|
|||
|
|
} else {
|
|||
|
|
setOutput("✅ 服务已启动\n" + out);
|
|||
|
|
}
|
|||
|
|
setTimeout(() => refreshStatus(), 500);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 停止服务
|
|||
|
|
function stopService() {
|
|||
|
|
setOutput("⏳ 正在停止服务...");
|
|||
|
|
runCommand(["systemctl", "stop", SERVICE + ".service"], (out, err) => {
|
|||
|
|
if (err) {
|
|||
|
|
setOutput("❌ 停止失败: " + (err.message || err) + "\n" + out);
|
|||
|
|
} else {
|
|||
|
|
setOutput("✅ 服务已停止\n" + out);
|
|||
|
|
}
|
|||
|
|
setTimeout(() => refreshStatus(), 500);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重启服务
|
|||
|
|
function restartService() {
|
|||
|
|
setOutput("⏳ 正在重启服务...");
|
|||
|
|
runCommand(["systemctl", "restart", SERVICE + ".service"], (out, err) => {
|
|||
|
|
if (err) {
|
|||
|
|
setOutput("❌ 重启失败: " + (err.message || err) + "\n" + out);
|
|||
|
|
} else {
|
|||
|
|
setOutput("✅ 服务已重启\n" + out);
|
|||
|
|
}
|
|||
|
|
setTimeout(() => refreshStatus(), 800);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查看日志
|
|||
|
|
function viewLog() {
|
|||
|
|
setOutput("⏳ 读取日志文件...");
|
|||
|
|
runCommand(["tail", "-n", "80", LOG_FILE], (out, err) => {
|
|||
|
|
if (err) {
|
|||
|
|
setOutput("❌ 无法读取日志: " + (err.message || err) + "\n尝试查看服务日志...");
|
|||
|
|
// 备用:查看 journalctl
|
|||
|
|
runCommand(["journalctl", "-u", SERVICE + ".service", "-n", "50", "--no-pager"], (out2, err2) => {
|
|||
|
|
if (err2) {
|
|||
|
|
setOutput("📭 日志为空或无法读取\n" + (out2 || ""));
|
|||
|
|
} else {
|
|||
|
|
setOutput("📋 系统日志 (journalctl):\n" + (out2 || "无日志"));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
setOutput("📋 服务日志 (最近80行):\n" + (out || "日志为空"));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 单独启动 web.py
|
|||
|
|
function startWebPy() {
|
|||
|
|
setOutput("⏳ 正在启动 web.py...");
|
|||
|
|
runCommand([
|
|||
|
|
"bash", "-c",
|
|||
|
|
"export DISPLAY=:0 && cd /home/ztl/LJ360 && sudo -u ztl python3 /home/ztl/LJ360/web.py >> /home/ztl/LJ360/web.log 2>&1 &"
|
|||
|
|
], (out, err) => {
|
|||
|
|
if (err) {
|
|||
|
|
setOutput("❌ 启动 web.py 失败: " + (err.message || err));
|
|||
|
|
} else {
|
|||
|
|
setOutput("✅ web.py 已在后台启动\n可通过 'ps aux | grep web.py' 查看进程");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 停止 web.py
|
|||
|
|
function stopWebPy() {
|
|||
|
|
setOutput("⏳ 正在关闭 web.py...");
|
|||
|
|
runCommand(["bash", "-c", "pkill -f /home/ztl/LJ360/web.py"], (out, err) => {
|
|||
|
|
if (err) {
|
|||
|
|
// pkill 没有找到进程也会返回错误,这是正常的
|
|||
|
|
if (err.message && err.message.includes("exit code 1")) {
|
|||
|
|
setOutput("⚠️ 未找到运行中的 web.py 进程");
|
|||
|
|
} else {
|
|||
|
|
setOutput("❌ 关闭失败: " + (err.message || err));
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
setOutput("✅ web.py 已关闭");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 事件绑定
|
|||
|
|
function bindEvents() {
|
|||
|
|
const btnStart = document.getElementById("btn-start");
|
|||
|
|
const btnStop = document.getElementById("btn-stop");
|
|||
|
|
const btnRestart = document.getElementById("btn-restart");
|
|||
|
|
const btnStatus = document.getElementById("btn-status");
|
|||
|
|
const btnLog = document.getElementById("btn-log");
|
|||
|
|
const btnPyon = document.getElementById("btn-pyon");
|
|||
|
|
const btnPyoff = document.getElementById("btn-pyoff");
|
|||
|
|
|
|||
|
|
if (btnStart) btnStart.addEventListener("click", startService);
|
|||
|
|
if (btnStop) btnStop.addEventListener("click", stopService);
|
|||
|
|
if (btnRestart) btnRestart.addEventListener("click", restartService);
|
|||
|
|
if (btnStatus) btnStatus.addEventListener("click", refreshStatus);
|
|||
|
|
if (btnLog) btnLog.addEventListener("click", viewLog);
|
|||
|
|
if (btnPyon) btnPyon.addEventListener("click", startWebPy);
|
|||
|
|
if (btnPyoff) btnPyoff.addEventListener("click", stopWebPy);
|
|||
|
|
|
|||
|
|
// 开关事件:开机自启变更
|
|||
|
|
if (autostartSwitch) {
|
|||
|
|
autostartSwitch.addEventListener("change", function(e) {
|
|||
|
|
setAutostart(e.target.checked);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 页面初始化
|
|||
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|||
|
|
initElements();
|
|||
|
|
bindEvents();
|
|||
|
|
refreshStatus();
|
|||
|
|
});
|