mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-27 16:54:04 +00:00
Add Basic Auth for /metrics
This commit is contained in:
parent
cc6f1d7487
commit
209fa83cff
6 changed files with 141 additions and 47 deletions
16
package-lock.json
generated
16
package-lock.json
generated
|
@ -625,6 +625,14 @@
|
|||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
|
||||
},
|
||||
"basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"bcrypt": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz",
|
||||
|
@ -1294,6 +1302,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"express-basic-auth": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.0.tgz",
|
||||
"integrity": "sha512-iJ0h1Gk6fZRrFmO7tP9nIbxwNgCUJASfNj5fb0Hy15lGtbqqsxpt7609+wq+0XlByZjXmC/rslWQtnuSTVRIcg==",
|
||||
"requires": {
|
||||
"basic-auth": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"command-exists": "^1.2.9",
|
||||
"dayjs": "^1.10.6",
|
||||
"express": "^4.17.1",
|
||||
"express-basic-auth": "^1.2.0",
|
||||
"form-data": "^4.0.0",
|
||||
"http-graceful-shutdown": "^3.1.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
|
|
40
server/auth.js
Normal file
40
server/auth.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
const basicAuth = require('express-basic-auth')
|
||||
const passwordHash = require('./password-hash');
|
||||
const {R} = require("redbean-node");
|
||||
|
||||
/**
|
||||
*
|
||||
* @param username : string
|
||||
* @param password : string
|
||||
* @returns {Promise<Bean|null>}
|
||||
*/
|
||||
exports.login = async function (username, password) {
|
||||
let user = await R.findOne("user", " username = ? AND active = 1 ", [
|
||||
username
|
||||
])
|
||||
|
||||
if (user && passwordHash.verify(password, user.password)) {
|
||||
// Upgrade the hash to bcrypt
|
||||
if (passwordHash.needRehash(user.password)) {
|
||||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
|
||||
passwordHash.generate(password),
|
||||
user.id
|
||||
]);
|
||||
}
|
||||
return user;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function myAuthorizer(username, password, callback) {
|
||||
exports.login(username, password).then((user) => {
|
||||
callback(null, user != null)
|
||||
})
|
||||
}
|
||||
|
||||
exports.basicAuth = basicAuth({
|
||||
authorizer: myAuthorizer,
|
||||
authorizeAsync: true,
|
||||
challenge: true
|
||||
});
|
|
@ -1,4 +1,3 @@
|
|||
const Prometheus = require('prom-client');
|
||||
const https = require('https');
|
||||
const dayjs = require("dayjs");
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
@ -6,6 +5,7 @@ var timezone = require('dayjs/plugin/timezone')
|
|||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
const axios = require("axios");
|
||||
const {Prometheus} = require("../prometheus");
|
||||
const {debug, UP, DOWN, PENDING} = require("../util");
|
||||
const {tcping, ping, checkCertificate} = require("../util-server");
|
||||
const {R} = require("redbean-node");
|
||||
|
@ -18,26 +18,6 @@ const customAgent = new https.Agent({
|
|||
maxCachedSessions: 0
|
||||
});
|
||||
|
||||
const commonLabels = [
|
||||
'monitor_name',
|
||||
'monitor_type',
|
||||
'monitor_url',
|
||||
'monitor_hostname',
|
||||
'monitor_port',
|
||||
]
|
||||
|
||||
const monitor_response_time = new Prometheus.Gauge({
|
||||
name: 'monitor_response_time',
|
||||
help: 'Monitor Response Time (ms)',
|
||||
labelNames: commonLabels
|
||||
});
|
||||
|
||||
const monitor_status = new Prometheus.Gauge({
|
||||
name: 'monitor_status',
|
||||
help: 'Monitor Status (1 = UP, 0= DOWN)',
|
||||
labelNames: commonLabels
|
||||
});
|
||||
|
||||
/**
|
||||
* status:
|
||||
* 0 = DOWN
|
||||
|
@ -76,13 +56,7 @@ class Monitor extends BeanModel {
|
|||
let previousBeat = null;
|
||||
let retries = 0;
|
||||
|
||||
const monitorLabelValues = {
|
||||
monitor_name: this.name,
|
||||
monitor_type: this.type,
|
||||
monitor_url: this.url,
|
||||
monitor_hostname: this.hostname,
|
||||
monitor_port: this.port
|
||||
}
|
||||
let prometheus = new Prometheus(this);
|
||||
|
||||
const beat = async () => {
|
||||
|
||||
|
@ -219,7 +193,6 @@ class Monitor extends BeanModel {
|
|||
bean.important = false;
|
||||
}
|
||||
|
||||
monitor_status.set(monitorLabelValues, bean.status)
|
||||
|
||||
if (bean.status === UP) {
|
||||
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`)
|
||||
|
@ -229,7 +202,7 @@ class Monitor extends BeanModel {
|
|||
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`)
|
||||
}
|
||||
|
||||
monitor_response_time.set(monitorLabelValues, bean.ping)
|
||||
prometheus.update(bean)
|
||||
|
||||
io.to(this.user_id).emit("heartbeat", bean.toJSON());
|
||||
|
||||
|
|
59
server/prometheus.js
Normal file
59
server/prometheus.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
const PrometheusClient = require('prom-client');
|
||||
|
||||
const commonLabels = [
|
||||
'monitor_name',
|
||||
'monitor_type',
|
||||
'monitor_url',
|
||||
'monitor_hostname',
|
||||
'monitor_port',
|
||||
]
|
||||
|
||||
const monitor_response_time = new PrometheusClient.Gauge({
|
||||
name: 'monitor_response_time',
|
||||
help: 'Monitor Response Time (ms)',
|
||||
labelNames: commonLabels
|
||||
});
|
||||
|
||||
const monitor_status = new PrometheusClient.Gauge({
|
||||
name: 'monitor_status',
|
||||
help: 'Monitor Status (1 = UP, 0= DOWN)',
|
||||
labelNames: commonLabels
|
||||
});
|
||||
|
||||
class Prometheus {
|
||||
monitorLabelValues = {}
|
||||
|
||||
constructor(monitor) {
|
||||
this.monitorLabelValues = {
|
||||
monitor_name: monitor.name,
|
||||
monitor_type: monitor.type,
|
||||
monitor_url: monitor.url,
|
||||
monitor_hostname: monitor.hostname,
|
||||
monitor_port: monitor.port
|
||||
}
|
||||
}
|
||||
|
||||
update(heartbeat) {
|
||||
try {
|
||||
monitor_status.set(this.monitorLabelValues, heartbeat.status)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof heartbeat.ping === 'number') {
|
||||
monitor_response_time.set(this.monitorLabelValues, heartbeat.ping)
|
||||
} else {
|
||||
// Is it good?
|
||||
monitor_response_time.set(this.monitorLabelValues, -1)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Prometheus
|
||||
}
|
|
@ -5,7 +5,6 @@ const http = require('http');
|
|||
const { Server } = require("socket.io");
|
||||
const dayjs = require("dayjs");
|
||||
const {R} = require("redbean-node");
|
||||
const passwordHash = require('./password-hash');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const Monitor = require("./model/monitor");
|
||||
const fs = require("fs");
|
||||
|
@ -15,7 +14,9 @@ const gracefulShutdown = require('http-graceful-shutdown');
|
|||
const Database = require("./database");
|
||||
const {sleep} = require("./util");
|
||||
const args = require('args-parser')(process.argv);
|
||||
const apiMetrics = require('prometheus-api-metrics');
|
||||
const prometheusAPIMetrics = require('prometheus-api-metrics');
|
||||
const { basicAuth } = require("./auth");
|
||||
const {login} = require("./auth");
|
||||
const version = require('../package.json').version;
|
||||
const hostname = args.host || "0.0.0.0"
|
||||
const port = args.port || 3001
|
||||
|
@ -27,6 +28,9 @@ const app = express();
|
|||
const server = http.createServer(app);
|
||||
const io = new Server(server);
|
||||
app.use(express.json())
|
||||
const basicAuthRouter = express.Router();
|
||||
basicAuthRouter.use(basicAuth)
|
||||
app.use(basicAuthRouter)
|
||||
|
||||
/**
|
||||
* Total WebSocket client connected to server currently, no actual use
|
||||
|
@ -56,15 +60,27 @@ let needSetup = false;
|
|||
await initDatabase();
|
||||
|
||||
console.log("Adding route")
|
||||
|
||||
// Normal Router here
|
||||
|
||||
app.use('/', express.static("dist"));
|
||||
app.use(apiMetrics())
|
||||
|
||||
// Basic Auth Router here
|
||||
|
||||
// For testing
|
||||
basicAuthRouter.get('/test-auth', (req, res) => {
|
||||
res.end("OK")
|
||||
});
|
||||
|
||||
// Prometheus API metrics /metrics
|
||||
// With Basic Auth using the first user's username/password
|
||||
basicAuthRouter.use(prometheusAPIMetrics())
|
||||
|
||||
// Universal Route Handler, must be at the end
|
||||
app.get('*', function(request, response, next) {
|
||||
response.sendFile(process.cwd() + '/dist/index.html');
|
||||
});
|
||||
|
||||
|
||||
console.log("Adding socket handler")
|
||||
io.on('connection', async (socket) => {
|
||||
|
||||
|
@ -120,20 +136,9 @@ let needSetup = false;
|
|||
socket.on("login", async (data, callback) => {
|
||||
console.log("Login")
|
||||
|
||||
let user = await R.findOne("user", " username = ? AND active = 1 ", [
|
||||
data.username
|
||||
])
|
||||
|
||||
if (user && passwordHash.verify(data.password, user.password)) {
|
||||
|
||||
// Upgrade the hash to bcrypt
|
||||
if (passwordHash.needRehash(user.password)) {
|
||||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
|
||||
passwordHash.generate(data.password),
|
||||
user.id
|
||||
]);
|
||||
}
|
||||
let user = await login(data.username, data.password)
|
||||
|
||||
if (user) {
|
||||
await afterLogin(socket, user)
|
||||
|
||||
callback({
|
||||
|
|
Loading…
Reference in a new issue