websocket solution

This commit is contained in:
Break27 2023-10-09 19:45:22 +08:00
parent c536a1ac49
commit 1da10b3996
3 changed files with 43 additions and 40 deletions

View File

@ -20,10 +20,10 @@ class RemoteApi:
# ===== # =====
@exposed_http("POST", "/remote") @exposed_http("POST", "/remote/update")
async def __state_handler(self, _: Request) -> Response: async def __remote_update_handler(self, _: Request) -> Response:
return make_json_response({ return make_json_response({
"hosts": await self.__remote.get_state(), "update": await self.__remote.update(),
}) })
@exposed_http("POST", "/remote/control") @exposed_http("POST", "/remote/control")

View File

@ -5,22 +5,45 @@ import { $, $$$, tools } from "../tools.js";
import { fromNow } from "./relativeTime.js"; import { fromNow } from "./relativeTime.js";
let prev_state = {};
let loading = false; let loading = false;
let firstRun = true;
/********************************************************/ /********************************************************/
export function main() { export function main() {
loadRemoteApi(x => makeView(x));
setInterval(update, 10000);
$("rf").addEventListener("click", refresh); $("rf").addEventListener("click", refresh);
createWebSocket();
setInterval(updateOfflineTime, 5000);
} }
function update() { function createWebSocket() {
loadRemoteApi(x => x.forEach(y => updateState(y))); let address = `wss://${window.location.host}/api/ws`;
updateOfflineTime(); let socket = new WebSocket(address);
socket.onopen = () => {
console.log("WebSocket connection established.");
};
socket.onmessage = (e) => {
let { event_type, event } = JSON.parse(e.data);
if (event_type != "remote_state") {
return;
}
if (firstRun) { makeView(event); firstRun = false; }
else event.forEach(x => updateState(x));
};
socket.onerror = () => {
socket.close();
};
socket.onclose = () => {
console.log("Websocket connection lost. Retrying in 5 seconds.");
setTimeout(createWebSocket, 5000);
};
} }
function refresh(event) { function refresh(event) {
@ -30,8 +53,7 @@ function refresh(event) {
let icon = $$$("#rf div.icon")[0]; let icon = $$$("#rf div.icon")[0];
icon.classList.toggle("spin"); icon.classList.toggle("spin");
prev_state = {}; getApiUpdate(x => {
loadRemoteApi(x => {
x.forEach(y => updateState(y)); x.forEach(y => updateState(y));
setTimeout(() => { setTimeout(() => {
icon.classList.toggle("spin") icon.classList.toggle("spin")
@ -59,17 +81,15 @@ function guards(http) {
return true; return true;
} }
function loadRemoteApi(callback) { function getApiUpdate(callback) {
let http = tools.makeRequest("POST", "/api/remote", () => { let http = tools.makeRequest("POST", "/api/remote/update", () => {
let response = http.responseText; let response = http.responseText;
if (! guards(http)) return; if (! guards(http)) return;
if (! response) return; if (! response) return;
let hosts = JSON.parse(response).result.hosts; let update = JSON.parse(response).result.update;
let diff = stateDiff(hosts); callback(update);
callback(diff);
}); });
} }
@ -84,9 +104,7 @@ function actionPerform(target, action) {
let result = JSON.parse(response).result; let result = JSON.parse(response).result;
if (result.code != 0) { if (result.code != 0) {
delete prev_state[target];
let state = $$$(`.host[name='${target}'] span.state`)[0]; let state = $$$(`.host[name='${target}'] span.state`)[0];
state.innerHTML = '  ⦻  Failed'; state.innerHTML = '  ⦻  Failed';
setTimeout(update, 3000); setTimeout(update, 3000);
} }
@ -122,7 +140,7 @@ function makeView(hosts) {
child.innerHTML = ` child.innerHTML = `
<div> <div>
<div> <div>
<span class="bulb"></span> <span class="bulb"></span>
<span class="hostname">${host.name}</span> <span class="hostname">${host.name}</span>
</div> </div>
<span class="state"></span> <span class="state"></span>
@ -136,7 +154,7 @@ function makeView(hosts) {
button.classList.add("remote-action"); button.classList.add("remote-action");
button.setAttribute("action", action); button.setAttribute("action", action);
button.onclick = () => { button.onclick = () => {
child.setAttribute("state", "unknown"); child.setAttribute("state", "unknown");
child.querySelector("span.state") child.querySelector("span.state")
.innerHTML = '&nbsp;&nbsp;&DoubleRightArrow;&nbsp;&nbsp;' .innerHTML = '&nbsp;&nbsp;&DoubleRightArrow;&nbsp;&nbsp;'
+ action[0] + action.slice(1).toLowerCase(); + action[0] + action.slice(1).toLowerCase();
@ -153,7 +171,7 @@ function updateState(host, child) {
let separator = $("separator"); let separator = $("separator");
let element = child ?? $$$(`div.host[name='${host.name}']`)[0]; let element = child ?? $$$(`div.host[name='${host.name}']`)[0];
element.setAttribute("state", host.online ? 'online' : 'offline'); element.setAttribute("state", host.online ? 'online' : 'offline');
element.setAttribute("last-seen", host.last_seen); element.setAttribute("last-seen", host.last_seen);
if (host.online) { if (host.online) {
@ -175,18 +193,3 @@ function updateOfflineTime(name) {
state.innerHTML = '&nbsp;&nbsp;&ndash;&nbsp;&nbsp;' + fromNow(timestamp); state.innerHTML = '&nbsp;&nbsp;&ndash;&nbsp;&nbsp;' + fromNow(timestamp);
} }
} }
function stateDiff(hosts) {
let diff = [];
for (const host of hosts) {
if (! (host.name in prev_state)
|| prev_state[host.name].online != host.online)
{
diff.push(host);
}
prev_state[host.name] = host;
}
return diff;
}

View File

@ -26,7 +26,7 @@ export function fromNow(timestamp) {
for (const unit in Time) if (time >= Time[unit]) { for (const unit in Time) if (time >= Time[unit]) {
let value = Math.floor(time / Time[unit]); let value = Math.floor(time / Time[unit]);
return RTF.format(-value, unit); return RTF.format(-value, unit);
} }
return "just now"; return "just now";
} }