mirror of
https://github.com/gnmyt/myspeed.git
synced 2026-02-13 00:59:36 -06:00
Merge pull request #687 from gnmyt/features/prometheus
🔥 Prometheus Endpunkt hinzugefügt
This commit is contained in:
@@ -25,6 +25,8 @@ MySpeed ist eine Speedtest-Analyse-Software, welche die Geschwindigkeit deines I
|
||||
- 🗄️ Füge mehrere Server direkt zu einer MySpeed-Instanz hinzu
|
||||
- 🩺 Es lassen sich Healthchecks konfigurieren, welche dich bei Fehlern oder Ausfällen über E-Mail, Signal, WhatsApp oder Telegram benachrichtigen können
|
||||
- 📆 Testergebnisse lassen sich bis zu 30 Tage lang speichern
|
||||
- 🔥 Unterstützung für Prometheus und Grafana
|
||||
- 🗳️ Wähle zwischen Ookla, LibreSpeed und Cloudflare Speedtest-Servern
|
||||
- 💁 Erfahre mehr zu MySpeed auf unserer [Website](https://myspeed.dev)
|
||||
|
||||
### ⬇️ Installation
|
||||
|
||||
@@ -25,6 +25,8 @@ MySpeed is a speed test analysis software that records your internet speed for u
|
||||
- 🗄️ Add multiple servers directly to a MySpeed instance
|
||||
- 🩺 Configure health checks to notify you via email, Signal, WhatsApp, or Telegram in case of errors or downtime
|
||||
- 📆 Test results can be stored for up to 30 days
|
||||
- 🔥 Support for Prometheus and Grafana
|
||||
- 🗳️ Choose between Ookla, LibreSpeed and Cloudflare speed test servers
|
||||
- 💁 Learn more about MySpeed on our [website](https://myspeed.dev)
|
||||
|
||||
### ⬇️ Installation
|
||||
|
||||
61
package-lock.json
generated
61
package-lock.json
generated
@@ -17,6 +17,7 @@
|
||||
"express": "^4.19.2",
|
||||
"mysql2": "^3.9.7",
|
||||
"node-schedule": "^2.1.1",
|
||||
"prom-client": "^15.1.2",
|
||||
"sequelize": "^6.37.3",
|
||||
"sqlite3": "^5.1.7",
|
||||
"tmp": "^0.2.3"
|
||||
@@ -155,6 +156,14 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz",
|
||||
"integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
@@ -410,6 +419,11 @@
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bintrees": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz",
|
||||
"integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw=="
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
|
||||
@@ -2504,6 +2518,18 @@
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"node_modules/prom-client": {
|
||||
"version": "15.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.2.tgz",
|
||||
"integrity": "sha512-on3h1iXb04QFLLThrmVYg1SChBQ9N1c+nKAjebBjokBqipddH3uxmOUcEkTnzmJ8Jh/5TSUnUqS40i2QB2dJHQ==",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
"tdigest": "^0.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16 || ^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/promise-inflight": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||
@@ -3281,6 +3307,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tdigest": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz",
|
||||
"integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==",
|
||||
"dependencies": {
|
||||
"bintrees": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
@@ -3691,6 +3725,11 @@
|
||||
"rimraf": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz",
|
||||
"integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w=="
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
@@ -3883,6 +3922,11 @@
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bintrees": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz",
|
||||
"integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw=="
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
|
||||
@@ -5438,6 +5482,15 @@
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"prom-client": {
|
||||
"version": "15.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.2.tgz",
|
||||
"integrity": "sha512-on3h1iXb04QFLLThrmVYg1SChBQ9N1c+nKAjebBjokBqipddH3uxmOUcEkTnzmJ8Jh/5TSUnUqS40i2QB2dJHQ==",
|
||||
"requires": {
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
"tdigest": "^0.1.1"
|
||||
}
|
||||
},
|
||||
"promise-inflight": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||
@@ -6001,6 +6054,14 @@
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"tdigest": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz",
|
||||
"integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==",
|
||||
"requires": {
|
||||
"bintrees": "1.0.2"
|
||||
}
|
||||
},
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"express": "^4.19.2",
|
||||
"mysql2": "^3.9.7",
|
||||
"node-schedule": "^2.1.1",
|
||||
"prom-client": "^15.1.2",
|
||||
"sequelize": "^6.37.3",
|
||||
"sqlite3": "^5.1.7",
|
||||
"tmp": "^0.2.3"
|
||||
|
||||
@@ -144,4 +144,19 @@ module.exports.removeOld = async () => {
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports.getLatest = async () => {
|
||||
let latest = await tests.findOne({order: [["created", "DESC"]]});
|
||||
if (latest.error === null) delete latest.error;
|
||||
if (latest.resultId === null) delete latest.resultId;
|
||||
return latest;
|
||||
}
|
||||
|
||||
module.exports.getLatest = async () => {
|
||||
let latest = await tests.findOne({order: [["created", "DESC"]]});
|
||||
if (latest === null) return undefined;
|
||||
if (latest.error === null) delete latest.error;
|
||||
if (latest.resultId === null) delete latest.resultId;
|
||||
return latest;
|
||||
}
|
||||
@@ -24,6 +24,7 @@ app.use("/api/storage", require('./routes/storage'));
|
||||
app.use("/api/recommendations", require('./routes/recommendations'));
|
||||
app.use("/api/nodes", require('./routes/nodes'));
|
||||
app.use("/api/integrations", require('./routes/integrations'));
|
||||
app.use("/api/prometheus", require('./routes/prometheus'));
|
||||
app.use("/api*", (req, res) => res.status(404).json({message: "Route not found"}));
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
|
||||
49
server/routes/prometheus.js
Normal file
49
server/routes/prometheus.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const express = require('express');
|
||||
const app = express.Router();
|
||||
const testController = require('../controller/speedtests');
|
||||
const promClient = require('prom-client');
|
||||
const config = require('../controller/config');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const pingGauge = new promClient.Gauge({name: 'myspeed_ping', help: 'Current ping in ms'});
|
||||
const downloadGauge = new promClient.Gauge({name: 'myspeed_download', help: 'Current download speed in Mbps'});
|
||||
const uploadGauge = new promClient.Gauge({name: 'myspeed_upload', help: 'Current upload speed in Mbps'});
|
||||
const currentServerGauge = new promClient.Gauge({name: 'myspeed_server', help: 'Current server ID',});
|
||||
const timeGauge = new promClient.Gauge({name: 'myspeed_time', help: 'Time of the test'});
|
||||
|
||||
app.get('/metrics', async (req, res) => {
|
||||
let passwordHash = await config.getValue("password");
|
||||
|
||||
if (passwordHash !== "none") {
|
||||
if (!req.headers.authorization || req.headers.authorization.indexOf('Basic ') === -1) {
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm"');
|
||||
return res.status(401).end('Unauthorized');
|
||||
}
|
||||
|
||||
const base64Credentials = req.headers.authorization.split(' ')[1];
|
||||
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
|
||||
const [username, password] = credentials.split(':');
|
||||
|
||||
if (username !== "prometheus" || !bcrypt.compareSync(password, passwordHash)) {
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm"');
|
||||
return res.status(401).end('Unauthorized');
|
||||
}
|
||||
}
|
||||
|
||||
const latest = await testController.getLatest();
|
||||
if (!latest) return res.status(500).end('No test found');
|
||||
|
||||
if (latest.error || latest.ping === -1)
|
||||
return res.status(500).end('Error in the latest test');
|
||||
|
||||
pingGauge.set(latest.ping);
|
||||
downloadGauge.set(latest.download);
|
||||
uploadGauge.set(latest.upload);
|
||||
currentServerGauge.set(latest.serverId);
|
||||
timeGauge.set(latest.time);
|
||||
|
||||
res.set('Content-Type', promClient.register.contentType);
|
||||
res.end(await promClient.register.metrics());
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
Reference in New Issue
Block a user