mirror of
https://github.com/louislam/uptime-kuma.git
synced 2026-01-31 11:03:11 +08:00
WIP
This commit is contained in:
@@ -104,9 +104,10 @@ module.exports = {
|
||||
"jsdoc/require-returns-type": "off",
|
||||
"jsdoc/require-param-type": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"prefer-const": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
"eqeqeq": "off",
|
||||
eqeqeq: "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -4,7 +4,6 @@ const Database = require("../server/database");
|
||||
const { R } = require("redbean-node");
|
||||
const readline = require("readline");
|
||||
const { passwordStrength } = require("check-password-strength");
|
||||
const { initJWTSecret } = require("../server/util-server");
|
||||
const User = require("../server/model/user");
|
||||
const { io } = require("socket.io-client");
|
||||
const { localWebSocketURL } = require("../server/config");
|
||||
@@ -62,8 +61,8 @@ const main = async () => {
|
||||
if (!("dry-run" in args)) {
|
||||
await User.resetPassword(user.id, password);
|
||||
|
||||
// Reset all sessions by reset jwt secret
|
||||
await initJWTSecret();
|
||||
// TODO: Reset all sessions by reset jwt secret
|
||||
// await initJWTSecret();
|
||||
|
||||
// Disconnect all other socket clients of the user
|
||||
await disconnectAllSocketClients(user.username, password);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
// @ts-ignore
|
||||
import * as Database from "./database.js";
|
||||
import { genSecret, log } from "../src/util";
|
||||
import { R } from "redbean-node";
|
||||
@@ -64,7 +65,20 @@ export function getAuthSecret() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get session from cookie
|
||||
* @param cookie Cookie string
|
||||
* @returns Session Object
|
||||
*/
|
||||
export function getSession(cookie: string) {
|
||||
const context = {
|
||||
headers: new Headers(),
|
||||
};
|
||||
context.headers.set("cookie", cookie || "");
|
||||
return auth.api.getSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
export async function createUser() {
|
||||
await auth.api.signUpEmail({
|
||||
@@ -76,9 +90,10 @@ export async function createUser() {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO
|
||||
createUser();
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO
|
||||
*/
|
||||
export async function migrateUser() {}
|
||||
|
||||
@@ -31,22 +31,6 @@ class User extends BeanModel {
|
||||
|
||||
this.password = hashedPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JWT for a user
|
||||
* @param {User} user The User to create a JsonWebToken for
|
||||
* @param {string} jwtSecret The key used to sign the JsonWebToken
|
||||
* @returns {string} the JsonWebToken as a string
|
||||
*/
|
||||
static createJWT(user, jwtSecret) {
|
||||
return jwt.sign(
|
||||
{
|
||||
username: user.username,
|
||||
h: shake256(user.password, SHAKE256_LENGTH),
|
||||
},
|
||||
jwtSecret
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User;
|
||||
|
||||
@@ -274,6 +274,7 @@ class RealBrowserMonitorType extends MonitorType {
|
||||
await page.waitForTimeout(monitor.screenshot_delay);
|
||||
}
|
||||
|
||||
// TODO fix without jwtSecret
|
||||
let filename = jwt.sign(monitor.id, server.jwtSecret) + ".png";
|
||||
|
||||
await page.screenshot({
|
||||
|
||||
@@ -105,7 +105,6 @@ const {
|
||||
getSettings,
|
||||
setSettings,
|
||||
setting,
|
||||
initJWTSecret,
|
||||
checkLogin,
|
||||
doubleCheckPassword,
|
||||
shake256,
|
||||
@@ -227,7 +226,7 @@ let needSetup = false;
|
||||
}
|
||||
|
||||
// Init Better Auth
|
||||
const { auth } = await import("./better-auth");
|
||||
const { auth, getSession } = await import("./better-auth");
|
||||
|
||||
// Database should be ready now
|
||||
await server.initAfterDatabaseReady();
|
||||
@@ -377,59 +376,14 @@ let needSetup = false;
|
||||
socket.emit("setup");
|
||||
}
|
||||
|
||||
// Auth Session
|
||||
const session = await getSession(socket.request.headers.cookie);
|
||||
|
||||
// ***************************
|
||||
// Public Socket API
|
||||
// ***************************
|
||||
|
||||
socket.on("loginByToken", async (token, callback) => {
|
||||
const clientIP = await server.getClientIP(socket);
|
||||
|
||||
log.info("auth", `Login by token. IP=${clientIP}`);
|
||||
|
||||
try {
|
||||
let decoded = jwt.verify(token, server.jwtSecret);
|
||||
|
||||
log.info("auth", "Username from JWT: " + decoded.username);
|
||||
|
||||
let user = await R.findOne("user", " username = ? AND active = 1 ", [decoded.username]);
|
||||
|
||||
if (user) {
|
||||
// Check if the password changed
|
||||
if (decoded.h !== shake256(user.password, SHAKE256_LENGTH)) {
|
||||
throw new Error("The token is invalid due to password change or old token");
|
||||
}
|
||||
|
||||
log.debug("auth", "afterLogin");
|
||||
await afterLogin(socket, user);
|
||||
log.debug("auth", "afterLogin ok");
|
||||
|
||||
log.info("auth", `Successfully logged in user ${decoded.username}. IP=${clientIP}`);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
});
|
||||
} else {
|
||||
log.info("auth", `Inactive or deleted user ${decoded.username}. IP=${clientIP}`);
|
||||
|
||||
callback({
|
||||
ok: false,
|
||||
msg: "authUserInactiveOrDeleted",
|
||||
msgi18n: true,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
log.error("auth", `Invalid token. IP=${clientIP}`);
|
||||
if (error.message) {
|
||||
log.error("auth", error.message, `IP=${clientIP}`);
|
||||
}
|
||||
callback({
|
||||
ok: false,
|
||||
msg: "authInvalidToken",
|
||||
msgi18n: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: better-auth
|
||||
socket.on("login", async (data, callback) => {
|
||||
const clientIP = await server.getClientIP(socket);
|
||||
|
||||
@@ -1412,6 +1366,7 @@ let needSetup = false;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: better-auth
|
||||
socket.on("changePassword", async (password, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
@@ -1838,24 +1793,6 @@ async function initDatabase(testMode = false) {
|
||||
|
||||
// Patch the database
|
||||
await Database.patch(port, hostname);
|
||||
|
||||
let jwtSecretBean = await R.findOne("setting", " `key` = ? ", ["jwtSecret"]);
|
||||
|
||||
if (!jwtSecretBean) {
|
||||
log.info("server", "JWT secret is not found, generate one.");
|
||||
jwtSecretBean = await initJWTSecret();
|
||||
log.info("server", "Stored JWT secret into database");
|
||||
} else {
|
||||
log.debug("server", "Load JWT secret from database.");
|
||||
}
|
||||
|
||||
// If there is no record in user table, it is a new Uptime Kuma instance, need to setup
|
||||
if ((await R.knex("user").count("id as count").first()).count === 0) {
|
||||
log.info("server", "No user, need setup");
|
||||
needSetup = true;
|
||||
}
|
||||
|
||||
server.jwtSecret = jwtSecretBean.value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ const fs = require("fs");
|
||||
const http = require("http");
|
||||
const { Server } = require("socket.io");
|
||||
const { R } = require("redbean-node");
|
||||
const { log, isDev } = require("../src/util");
|
||||
const { log, isDev, devOriginList } = require("../src/util");
|
||||
const Database = require("./database");
|
||||
const util = require("util");
|
||||
const { Settings } = require("./settings");
|
||||
@@ -54,12 +54,6 @@ class UptimeKumaServer {
|
||||
*/
|
||||
static monitorTypeList = {};
|
||||
|
||||
/**
|
||||
* Use for decode the auth object
|
||||
* @type {null}
|
||||
*/
|
||||
jwtSecret = null;
|
||||
|
||||
/**
|
||||
* Get the current instance of the server if it exists, otherwise
|
||||
* create a new instance.
|
||||
@@ -135,12 +129,15 @@ class UptimeKumaServer {
|
||||
let cors = undefined;
|
||||
if (isDev) {
|
||||
cors = {
|
||||
origin: "*",
|
||||
origin: devOriginList,
|
||||
credentials: true,
|
||||
methods: ["GET", "POST"],
|
||||
};
|
||||
}
|
||||
|
||||
this.io = new Server(this.httpServer, {
|
||||
cors,
|
||||
cookie: true,
|
||||
allowRequest: async (req, callback) => {
|
||||
let transport;
|
||||
// It should be always true, but just in case, because this property is not documented
|
||||
|
||||
@@ -34,31 +34,6 @@ const { Kafka, SASLOptions } = require("kafkajs");
|
||||
const crypto = require("crypto");
|
||||
|
||||
const isWindows = process.platform === /^win/.test(process.platform);
|
||||
/**
|
||||
* Init or reset JWT secret
|
||||
* @returns {Promise<Bean>} JWT secret
|
||||
*/
|
||||
exports.initJWTSecret = async () => {
|
||||
let jwtSecretBean = await R.findOne("setting", " `key` = ? ", ["jwtSecret"]);
|
||||
|
||||
if (!jwtSecretBean) {
|
||||
jwtSecretBean = R.dispense("setting");
|
||||
jwtSecretBean.key = "jwtSecret";
|
||||
}
|
||||
|
||||
jwtSecretBean.value = await passwordHash.generate(genSecret());
|
||||
await R.store(jwtSecretBean);
|
||||
return jwtSecretBean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a jwt and returns the payload portion without verifying the jwt.
|
||||
* @param {string} jwt The input jwt as a string
|
||||
* @returns {object} Decoded jwt payload object
|
||||
*/
|
||||
exports.decodeJwt = (jwt) => {
|
||||
return JSON.parse(Buffer.from(jwt.split(".")[1], "base64").toString());
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an Access Token from an oidc/oauth2 provider
|
||||
|
||||
@@ -117,7 +117,9 @@ export default {
|
||||
url = undefined;
|
||||
}
|
||||
|
||||
socket = io(url);
|
||||
socket = io(url, {
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
socket.on("info", (info) => {
|
||||
this.info = info;
|
||||
|
||||
@@ -28,6 +28,13 @@ export const isNode = typeof process !== "undefined" && process?.versions?.node;
|
||||
*/
|
||||
const dayjs = isNode ? require("dayjs") : dayjsFrontend;
|
||||
|
||||
export const devOriginList = [
|
||||
"http://127.0.0.1:3000",
|
||||
"http://127.0.0.1:3001",
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3001",
|
||||
];
|
||||
|
||||
export const appName = "Uptime Kuma";
|
||||
export const DOWN = 0;
|
||||
export const UP = 1;
|
||||
|
||||
Reference in New Issue
Block a user