Skip to content

Commit

Permalink
Use an actual config, systemd template, minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
virtulis committed Nov 3, 2024
1 parent 5dfe43f commit c1cc4a0
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 56 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
/node_modules

/.idea

/config.js
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ Available at [this.computer](https://this.computer).
* Have a hostname for the website.
* Have two hostnames for the IP checks, one with just an A record, one with just AAAA.

Do this (substitute the hostnames):
Copy `config.example.js` to `config.js` and edit as needed.

```bash
# install the few dependencies (pnpm is preferred but npm will work too)
pnpm install

# substitute the hostnames. $PWD/dist is where the html file is
node build.js this.computer 4.this.computer 6.this.computer $PWD/dist
# this generates `dist/index.html` and nginx configs in conf/
node build.js

# LetsEncrypt setup (skip and edit the main config if you prefer handling it yourself)

Expand All @@ -32,8 +34,14 @@ sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx.sh
sudo cp conf/this-computer.conf /etc/nginx/sites-enabled/
sudo systemctl reload nginx

# Enable the pinger service
sudo systemctl enable --now $PWD/conf/this-computer-pong.service


```



## License

MIT
31 changes: 15 additions & 16 deletions build.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import { mkdir, readdir, readFile, writeFile } from 'fs/promises';
import { mkdir, readdir, readFile, writeFile, realpath } from 'fs/promises';

async function main() {

const args = process.argv.slice(2);

if (args.length != 4) {
console.log('Usage: node build.js base_host ip4_host ip6_host root_dir');
return;
}

const [BASE_HOST, IP4_HOST, IP6_HOST, DIST_DIR] = args;
const vars = { BASE_HOST, IP4_HOST, IP6_HOST, DIST_DIR };
console.log(vars);


const { default: config } = await import('./config.js');

await mkdir('dist', { recursive: true });
await mkdir('conf', { recursive: true });


const DIST_DIR = await realpath('dist');
const CONF_DIR = await realpath('conf');
const [BASE_HOST, IP4_HOST, IP6_HOST, PONG_PORT] = [config.baseHost, config.ipv4Host, config.ipv4Host, config.pongServerPort];
const CONFIG_JSON = JSON.stringify(config);

const vars = { BASE_HOST, IP4_HOST, IP6_HOST, DIST_DIR, CONF_DIR, PONG_PORT, CONFIG_JSON };
console.log(vars);

const files = await readdir('template');
for (const fn of files) {
let src = await readFile(`template/${fn}`, 'utf-8');
for (const [key, val] of Object.entries(vars)) {
src = src.replaceAll(key, val);
}
const out = fn.match(/\.conf$/) ? 'conf' : 'dist';
const out = fn.match(/\.(conf|service)$/) ? 'conf' : 'dist';
await writeFile(`${out}/${fn}`, src);
}

}

await main();
29 changes: 29 additions & 0 deletions config.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const baseHost = 'this.computer';

export default {

baseHost,

// This should only have an A record.
ipv4Host: `4.${baseHost}`,
// This should only have an AAAA record.
ipv6Host: `6.${baseHost}`,

graphWindow: 30_000,
jitterWindow: 10_000,
graphScale: 500, // ms

pingServers: [
{
label: 'EU',
host: baseHost,
},
/*{
label: 'EU',
host: baseHost,
},*/
],

pingServerPort: 1234,

};
85 changes: 51 additions & 34 deletions template/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,27 @@
}
#graphs {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 16px;
padding: 16px 0;
width: 100%;
}
.graph {
display: flex;
flex-direction: column;
gap: 4px;
width: 100%;
max-width: 500px;
}
.legend {
display: flex;
gap: 16px;
gap: 8px;
align-items: center;
flex-wrap: wrap;
max-width: 500px;
}
.legend span {
min-width: 80px;
min-width: 100px;
}
canvas {
width: 100%;
Expand Down Expand Up @@ -89,19 +97,22 @@ <h2>This Computer:</h2>

<footer>
100% <a href="https://kludge.guru">kludges</a>.
<a href="https://loud.computer/@virtulis/111606765891568617">Share</a>!
<a href="https://loud.computer/@virtulis/113415168353910319">Share</a>!
<a href="https://github.com/virtulis/this-computer">Github</a>.
<a href="https://ko-fi.com/kludge_guru">Ko-fi</a>.
</footer>

<script type="application/javascript">

const config = CONFIG_JSON;
const { graphWindow, jitterWindow, pingServers, graphScale, ipv4Host, ipv6Host } = config;

const timeout = new Promise(resolve => setTimeout(() => resolve(null), 30_000));

const ips = {};
const promises = {
4: Promise.race([timeout, fetch('https://IP4_HOST/').then(res => res.text()).catch(() => null)]),
6: Promise.race([timeout, fetch('https://IP6_HOST/').then(res => res.text()).catch(() => null)]),
4: Promise.race([timeout, fetch(`https://${ipv4Host}/`).then(res => res.text()).catch(() => null)]),
6: Promise.race([timeout, fetch(`https://${ipv6Host}/`).then(res => res.text()).catch(() => null)]),
};
Object.entries(promises).forEach(([v, p]) => p.then(str => {
ips[v] = str?.trim();
Expand All @@ -120,27 +131,23 @@ <h2>This Computer:</h2>
e.target.textContent = 'Copied';
}));

const pingServers = [{
label: '???',
host: 'BASE_HOST'
}];

const graphsCtr = document.getElementById('graphs');
const debug = document.getElementById('debug');
const bufLen = 30 * 5;

for (const [i, { label, host }] of pingServers.entries()) {
for (const { label, host } of pingServers) {

let ws;
let backoff = 1;
let pinger;
let interval = 200;
let worst = 500;
let bufLen = Math.ceil(graphWindow / interval + 1);
let changed = false;

const pings = [];
const recv = [];
const received = [];

const graph = document.createElement('graph');
graph.className = 'graph';
graphsCtr.append(graph);
const legend = document.createElement('div');
legend.className = 'legend';
const lblSpan = document.createElement('strong');
Expand All @@ -151,18 +158,18 @@ <h2>This Computer:</h2>
legend.append(s);
return s;
});
graphsCtr.append(legend);
graph.append(legend);

const canvas = document.createElement('canvas');
graph.append(canvas);
let dpr, cw, ch, style;
const resizeCanvas = () => {
const ctrW = graphsCtr.getBoundingClientRect().width;
const ctrW = canvas.getBoundingClientRect().width;
dpr = devicePixelRatio;
canvas.width = cw = Math.round(ctrW * dpr);
canvas.height = ch = Math.round(200 * dpr);
style = getComputedStyle(graphsCtr);
};
graphsCtr.append(canvas);
resizeCanvas();
window.addEventListener('resize', resizeCanvas);

Expand All @@ -173,22 +180,25 @@ <h2>This Computer:</h2>
const msg = JSON.parse(e.data);
if (msg.interval) {
interval = msg.interval;
bufLen = Math.ceil(graphWindow / interval + 1);
clearInterval(pinger);
pinger = setInterval(() => {
const at = Date.now();
const prev = pings[pings.length - 1];
const sentDiff = prev ? at - prev.at : interval;
ws.send(JSON.stringify({ ping: at }))
pings.push({ at, sentDiff, roundtrip: null, diff: null });
if (pings.length > bufLen) pings.shift();
while (pings.length > bufLen || (pings[0] && at - pings[0].at > graphWindow + interval)) {
pings.shift();
}
}, interval);
}
if (msg.ping) {
const at = Date.now();
const last = recv[recv.length - 1];
const last = received[received.length - 1];
const diff = !last ? 0 : (at - last.at) - interval;
recv.push({ at: Date.now(), ping: msg.ping, diff });
if (recv.length > bufLen) recv.shift();
received.push({ at: Date.now(), ping: msg.ping, diff });
if (received.length > bufLen) received.shift();
}
if (msg.pong) {
const ping = pings.find(p => p.at == msg.pong);
Expand All @@ -198,7 +208,6 @@ <h2>This Computer:</h2>
}
}
changed = true;
// debug.textContent = JSON.stringify({ pings, recv });
}
};
const closed = () => {
Expand All @@ -212,44 +221,52 @@ <h2>This Computer:</h2>
const paint = () => {

const mid = Math.round(ch / 2);
const tWin = bufLen * interval;
const t0 = Date.now() - tWin;
const t0 = Date.now() - graphWindow;
const bar = Math.ceil(cw / bufLen);
ctx.clearRect(0, 0, cw, ch);
ctx.fillStyle = style.getPropertyValue('--canvas-axis');
ctx.fillRect(0, mid, cw, 1);
ctx.fillStyle = style.getPropertyValue('--canvas-bars');
for (const ping of pings) {
const x = (ping.at - t0) / tWin * cw;
const h = ping.roundtrip ? ping.roundtrip / worst * ch : 1;
const y = mid - h / 2 + (ping.diff || 0) / worst;
const x = (ping.at - t0) / graphWindow * cw;
const h = ping.roundtrip ? ping.roundtrip / graphScale * ch : 1;
const y = mid - h / 2 + (ping.diff || 0) / graphScale;
ctx.fillRect(x, y, bar, h);
}
ctx.strokeStyle = style.getPropertyValue('--canvas-line');
ctx.lineWidth = dpr * 2;
ctx.beginPath();
for (const [i, ping] of recv.entries()) {
const x = (ping.at - t0) / tWin * cw;
const y = mid + (ping.diff || 0) / worst;
for (const [i, ping] of received.entries()) {
const x = (ping.at - t0) / graphWindow * cw;
const y = mid + (ping.diff || 0) / graphScale;
if (i) ctx.lineTo(x, y);
else ctx.moveTo(x, y);
}
ctx.stroke();

if (changed) {
let min = Infinity, max = 0, sum = 0, num = 0, jit = 0;
const jitCutoff = Date.now() - jitterWindow;
let prev;
for (const [i, ping] of pings.entries()) {
const rtt = ping.roundtrip;
if (typeof rtt != 'number') continue;
num++;
sum += rtt;
min = Math.min(min, rtt);
max = Math.max(max, rtt);
if (i) jit = Math.max(jit, Math.abs(ping.diff));
if (i && ping.at >= jitCutoff) {
jit = Math.max(jit, Math.abs(ping.diff), prev ? Math.abs(prev.roundtrip - rtt) : 0);
}
prev = ping;
}
for (const ping of received) {
if (ping.at < jitCutoff) continue;
jit = Math.max(jit, ping.diff);
}
if (num) {
const avg = sum / num;
avgSpan.textContent = `Avg; ${avg.toFixed(1)} ms`;
avgSpan.textContent = `Avg: ${avg.toFixed(1)} ms`;
minSpan.textContent = `Min: ${Math.round(min)} ms`;
maxSpan.textContent = `Max: ${Math.round(max)} ms`;
jitSpan.textContent = `Jitter: ${Math.round(jit)} ms`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Description=This.Computer Pong Server

[Service]
WorkingDirectory=/srv/this-computer
Environment="WS_PORT=1234"
WorkingDirectory=DIST_DIR
Environment="WS_PORT=PONG_PORT"
ExecStart=/usr/bin/node pong/server.js
Restart=on-failure

Expand Down
2 changes: 1 addition & 1 deletion template/this-computer.conf
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ server {
try_files $uri $uri.html =404;
}
location /ws {
proxy_pass http://127.0.0.1:1234;
proxy_pass http://127.0.0.1:PONG_PORT;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
Expand Down

0 comments on commit c1cc4a0

Please sign in to comment.