diff --git a/.gitignore b/.gitignore index 6642cf4..0f25aeb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /dist /conf +/node_modules /.idea diff --git a/package.json b/package.json index c5124bf..c9b945e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,8 @@ { "name": "@virtulis/this-computer", "private": true, - "type": "module" + "type": "module", + "dependencies": { + "ws": "^8.18.0" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..400d6a8 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,31 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + ws: + specifier: ^8.18.0 + version: 8.18.0 + +packages: + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + +snapshots: + + ws@8.18.0: {} diff --git a/pong/server.js b/pong/server.js new file mode 100644 index 0000000..9c882ab --- /dev/null +++ b/pong/server.js @@ -0,0 +1,68 @@ +import { WebSocketServer } from 'ws'; + +const verbose = !!process.env.WS_VERBOSE; +const port = process.env.WS_PORT ? Number(process.env.WS_PORT) : 1234; +const wss = new WebSocketServer({ port }); + +const timeoutMs = 60_000; +let connId = 1; + +const interval = 200; + +wss.on('connection', function connection(ws, req) { + + const id = connId++; + const ip = req.headers['x-forwarded-for'] ?? req.socket.remoteAddress; + + if (verbose) console.log('Connected:', id, ip); + + const send = msg => ws.send(JSON.stringify(msg)); + + let lastRecv = Date.now(); + let timer; + const sender = setInterval(() => { + send({ ping: Date.now() }); + }, interval); + + const bump = () => { + if (timer) clearTimeout(timer); + timer = setTimeout(() => { + if (verbose) console.log('Dead:', id, ip); + ws.close(); + }, timeoutMs); + } + bump(); + + if (verbose) ws.on('error', e => console.log('Error:', id, ip, e?.message ?? e)); + + ws.on('close', () => { + if (verbose) console.log('Closed:', id, ip); + clearTimeout(timer); + clearInterval(sender); + }); + + ws.on('message', function message(data) { + try { + if (verbose) console.log('Received:', id, ip, data); + const msg = JSON.parse(data); + const time = Date.now(); + const diff = time - lastRecv; + lastRecv = time; + send({ + pong: msg.ping, + time, + diff, + }); + } + catch (e) { + if (verbose) console.log('Error:', id, ip, e); + ws.close(); + } + }); + + send({ + ping: Date.now(), + interval, + }); + +}); diff --git a/template/index.html b/template/index.html index 6ee3517..a071725 100644 --- a/template/index.html +++ b/template/index.html @@ -9,6 +9,12 @@ body { font-family: sans-serif; padding: 16px; + --canvas-axis: #000; + --canvas-bars: #606; + --canvas-line: #090; + } + a { + color: #339; } footer { margin: 30px 0 10px; @@ -17,6 +23,42 @@ font-size: 20px; padding: 10px 15px; } + #graphs { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 0; + max-width: 500px; + } + .legend { + display: flex; + gap: 16px; + align-items: center; + flex-wrap: wrap; + } + .legend span { + min-width: 80px; + } + canvas { + width: 100%; + max-width: 500px; + } + @media (prefers-color-scheme: dark) { + body { + background: black; + color: white; + --canvas-axis: #fff; + --canvas-bars: #969; + --canvas-line: #6e6; + } + a { + color: #66a; + } + button { + background: transparent; + color: white; + } + } @@ -37,6 +79,12 @@

This Computer:

+
+ +
+
+ +
diff --git a/template/this-computer.conf b/template/this-computer.conf index 0f7cc0b..4f4b8ec 100644 --- a/template/this-computer.conf +++ b/template/this-computer.conf @@ -25,6 +25,14 @@ server { index index.html; try_files $uri $uri.html =404; } + location /ws { + proxy_pass http://127.0.0.1:1234; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_read_timeout 60m; + } }