Implement reset password, and add an internal betterAuth user for internal operations

This commit is contained in:
Louis Lam
2026-06-28 19:29:38 +08:00
parent 2fa2ab3c38
commit 183cf21f6f
8 changed files with 656 additions and 6 deletions
+119
View File
@@ -0,0 +1,119 @@
import { APIError } from "better-auth";
console.log("== Uptime Kuma Reset Password Tool ==");
import { loadEnvFile } from "node:process";
import { auth, BetterAuthUser, getGodKumaHeaders, removeOtherGodKumaUsers } from "../server/better-auth";
import { input, password as passwordInput, select } from "@inquirer/prompts";
import { ExitPromptError } from "@inquirer/core";
import { genSecret, isDevEnv } from "../src/util";
// @ts-ignore
import Database from "../server/database.js";
// @ts-ignore No type package is available
import parseArgs from "args-parser";
let kumaGodHeader: Headers;
/**
*
*/
async function main() {
const args = parseArgs(process.argv);
try {
loadEnvFile();
} catch {}
console.log("Dev Environment:", isDevEnv());
Database.initDataDir(args);
await Database.connect();
kumaGodHeader = await getGodKumaHeaders(false);
const result = await auth().api.listUsers({
query: {},
headers: kumaGodHeader,
});
const choices = result.users.map((user) => {
const email = user.email;
const username = (user as BetterAuthUser).username || email;
return {
name: username,
value: email,
description: "",
};
});
let email: string;
while (true) {
const selectEmail = await select({
message: "Select a username:",
choices,
});
if (!selectEmail) {
console.log("Invalid choice");
continue;
}
email = selectEmail;
break;
}
while (true) {
const password = await passwordInput({
message: "Enter new password:",
mask: "*",
});
try {
const ok = await auth().api.setUserPassword({
body: {
userId: email,
newPassword: password,
},
headers: kumaGodHeader,
});
if (ok) {
console.log("Password reset succesfully!");
break;
}
} catch (_) {
// Better Auth shows Cli error message in cli already, we don't need to
}
}
await cleanUp();
}
/**
*
*/
async function cleanUp() {
if (kumaGodHeader) {
// Better Auth is not allow to remove current user, so leave it as is first...
}
await Database.close();
}
// Gracefully handle Ctrl+C
process.on("SIGINT", async () => {
console.log("\nGracefully shutting down from SIGINT (Ctrl+C)");
await cleanUp();
process.exit(0);
});
try {
await main();
} catch (e) {
// Ignore prompt exit errors
if (!(e instanceof ExitPromptError)) {
throw e;
}
}
-1
View File
@@ -1 +0,0 @@
console.log("== Uptime Kuma Reset Password Tool ==");
+405
View File
@@ -10,6 +10,7 @@
"license": "MIT",
"dependencies": {
"@grpc/grpc-js": "~1.8.22",
"@inquirer/prompts": "~8.5.2",
"@louislam/ping": "~0.4.4-mod.1",
"@louislam/sqlite3": "15.1.6",
"@vvo/tzdb": "~6.198.0",
@@ -1259,6 +1260,368 @@
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@inquirer/ansi": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz",
"integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==",
"license": "MIT",
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
}
},
"node_modules/@inquirer/checkbox": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.2.1.tgz",
"integrity": "sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==",
"license": "MIT",
"dependencies": {
"@inquirer/ansi": "^2.0.7",
"@inquirer/core": "^11.2.1",
"@inquirer/figures": "^2.0.7",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/confirm": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz",
"integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.2.1",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/core": {
"version": "11.2.1",
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz",
"integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==",
"license": "MIT",
"dependencies": {
"@inquirer/ansi": "^2.0.7",
"@inquirer/figures": "^2.0.7",
"@inquirer/type": "^4.0.7",
"cli-width": "^4.1.0",
"fast-wrap-ansi": "^0.2.0",
"mute-stream": "^3.0.0",
"signal-exit": "^4.1.0"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/core/node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"license": "ISC",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@inquirer/editor": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.2.2.tgz",
"integrity": "sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.2.1",
"@inquirer/external-editor": "^3.0.3",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/expand": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.1.1.tgz",
"integrity": "sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.2.1",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/external-editor": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.3.tgz",
"integrity": "sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==",
"license": "MIT",
"dependencies": {
"chardet": "^2.1.1",
"iconv-lite": "^0.7.2"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/external-editor/node_modules/chardet": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz",
"integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==",
"license": "MIT"
},
"node_modules/@inquirer/external-editor/node_modules/iconv-lite": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/@inquirer/figures": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz",
"integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==",
"license": "MIT",
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
}
},
"node_modules/@inquirer/input": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.2.tgz",
"integrity": "sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.2.1",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/number": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.1.1.tgz",
"integrity": "sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.2.1",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/password": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.1.1.tgz",
"integrity": "sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==",
"license": "MIT",
"dependencies": {
"@inquirer/ansi": "^2.0.7",
"@inquirer/core": "^11.2.1",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/prompts": {
"version": "8.5.2",
"resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.5.2.tgz",
"integrity": "sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==",
"license": "MIT",
"dependencies": {
"@inquirer/checkbox": "^5.2.1",
"@inquirer/confirm": "^6.1.1",
"@inquirer/editor": "^5.2.2",
"@inquirer/expand": "^5.1.1",
"@inquirer/input": "^5.1.2",
"@inquirer/number": "^4.1.1",
"@inquirer/password": "^5.1.1",
"@inquirer/rawlist": "^5.3.1",
"@inquirer/search": "^4.2.1",
"@inquirer/select": "^5.2.1"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/rawlist": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.3.1.tgz",
"integrity": "sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.2.1",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/search": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.2.1.tgz",
"integrity": "sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.2.1",
"@inquirer/figures": "^2.0.7",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/select": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.1.tgz",
"integrity": "sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==",
"license": "MIT",
"dependencies": {
"@inquirer/ansi": "^2.0.7",
"@inquirer/core": "^11.2.1",
"@inquirer/figures": "^2.0.7",
"@inquirer/type": "^4.0.7"
},
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/type": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz",
"integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==",
"license": "MIT",
"engines": {
"node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@intlify/core-base": {
"version": "11.2.8",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.2.8.tgz",
@@ -5217,6 +5580,15 @@
"node": ">=6"
}
},
"node_modules/cli-width": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
"integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
"license": "ISC",
"engines": {
"node": ">= 12"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -7383,6 +7755,21 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-string-truncated-width": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
"integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
"license": "MIT"
},
"node_modules/fast-string-width": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
"integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
"license": "MIT",
"dependencies": {
"fast-string-truncated-width": "^3.0.2"
}
},
"node_modules/fast-unique-numbers": {
"version": "8.0.13",
"resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz",
@@ -7414,6 +7801,15 @@
],
"license": "BSD-3-Clause"
},
"node_modules/fast-wrap-ansi": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz",
"integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==",
"license": "MIT",
"dependencies": {
"fast-string-width": "^3.0.2"
}
},
"node_modules/fast-xml-parser": {
"version": "5.2.5",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
@@ -10583,6 +10979,15 @@
"node": ">=16"
}
},
"node_modules/mute-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz",
"integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==",
"license": "ISC",
"engines": {
"node": "^20.17.0 || >=22.9.0"
}
},
"node_modules/mysql2": {
"version": "3.11.5",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.5.tgz",
+3 -2
View File
@@ -43,7 +43,7 @@
"setup": "git checkout 2.4.0 && npm ci --omit dev --no-audit && node --run download-dist",
"download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "tsx extra/reset-password.ts",
"reset-password": "tsx extra/reset-password.mts",
"remove-2fa": "node extra/remove-2fa.js",
"simple-dns-server": "node extra/simple-dns-server.js",
"simple-mqtt-server": "node extra/simple-mqtt-server.js",
@@ -64,6 +64,7 @@
},
"dependencies": {
"@grpc/grpc-js": "~1.8.22",
"@inquirer/prompts": "~8.5.2",
"@louislam/ping": "~0.4.4-mod.1",
"@louislam/sqlite3": "15.1.6",
"@vvo/tzdb": "~6.198.0",
@@ -142,9 +143,9 @@
"tcp-ping": "~0.1.1",
"tldts": "~7.0.23",
"tough-cookie": "~4.1.4",
"tsx": "~4.22.3",
"validator": "~13.15.26",
"web-push": "~3.6.7",
"tsx": "~4.22.3",
"ws": "~8.19.0",
"zod": "~4.4.3"
},
+1
View File
@@ -7,6 +7,7 @@ const { Settings } = require("./settings");
const dayjs = require("dayjs");
/**
* @deprecated Use Better Auth
* Login to web app
* @param {string} username Username to login with
* @param {string} password Password to login with
+114 -1
View File
@@ -9,10 +9,13 @@ import { admin } from "better-auth/plugins";
import { Socket } from "socket.io";
import { haveIBeenPwned } from "better-auth/plugins";
import { twoFactor } from "better-auth/plugins";
import { createAuthMiddleware, APIError } from "better-auth/api";
type BetterAuthUser = ReturnType<typeof createAuthInstance>["$Infer"]["Session"]["user"];
export type BetterAuthUser = ReturnType<typeof createAuthInstance>["$Infer"]["Session"]["user"];
let authInstance: ReturnType<typeof createAuthInstance>;
let godKumaHeaders: Headers;
let godKumaInitSecret: string = "";
/**
*
@@ -46,6 +49,11 @@ function createAuthInstance() {
return betterAuth({
database,
// Just want to silent the warning message
// As we don't use callback/redirect, it is not used
baseURL: "http://localhost:3000",
secret: getAuthSecret(),
// Should be handled in Express.js, check better-auth-router.ts
trustedOrigins: ["*"],
@@ -100,6 +108,23 @@ function createAuthInstance() {
verification: {
modelName: "better_auth_verification",
},
hooks: {
before: createAuthMiddleware(async (ctx) => {
// God Kuma is not allowed to login from the outside, only for internal usage
if (ctx.path.startsWith("/sign-in/")) {
const username = ctx.body?.username;
const email = ctx.body?.email;
if (username?.startsWith("god_kuma_") || email?.endsWith("@god.uptime-kuma.internal")) {
if (!godKumaInitSecret || ctx.headers?.get("god-kuma-init-secret") != godKumaInitSecret) {
throw new APIError("BAD_REQUEST", {
message: "God Kuma is not allowed to login from the outside.",
});
}
}
}
}),
},
});
}
@@ -228,3 +253,91 @@ export async function migrateUser() {
// TODO: User have to input pwd one time to migrate, or we can not get the original password hash to create a better-auth user
// TODO: Disable Auth may need to directly create a user in the database
}
/**
* Because there is no way to do admin operations without admin session
* We have to create a god admin user to do operations
* @param removeExistingGodKuma For server restart, remove previous god kuma user. But for short time scripts like reset password, you probably don't want to remove.
* @returns Headers for the temp admin user
*/
export async function getGodKumaHeaders(removeExistingGodKuma = true): Promise<Headers> {
if (!godKumaHeaders) {
const username = "god_kuma_" + genSecret(16);
const password = genSecret();
const email = `${username}@god.uptime-kuma.internal`;
await auth().api.createUser({
body: {
name: "God Kuma (Temp)",
email: email,
password,
role: "admin",
data: {
username,
},
},
});
godKumaInitSecret = genSecret();
// Sign in
const response = await auth().api.signInEmail({
body: {
email,
password,
},
headers: {
"god-kuma-init-secret": godKumaInitSecret,
},
asResponse: true,
});
const header = new Headers();
header.set("cookie", response.headers.get("set-cookie") || "");
header.set("god-kuma-email", email);
godKumaHeaders = header;
if (removeExistingGodKuma) {
await removeOtherGodKumaUsers();
}
}
return godKumaHeaders;
}
/**
* Remove other god kuma users
*/
export async function removeOtherGodKumaUsers() {
let searchValue = "@god.uptime-kuma.internal";
let email = godKumaHeaders.get("god-kuma-email");
if (!email) {
throw new Error("Unexpected error: God Kuma email not found in headers.");
}
const result = await auth().api.listUsers({
query: {
searchField: "email",
searchValue,
searchOperator: "ends_with",
},
headers: godKumaHeaders,
});
for (const user of result.users) {
if (user.email === email) {
continue;
}
log.debug("auth", "Removing existing god kuma user:", user.email);
// Delete the user
await auth().api.removeUser({
body: {
userId: user.id,
},
headers: godKumaHeaders,
});
}
}
+2 -2
View File
@@ -2,7 +2,7 @@ const fs = require("fs");
const fsAsync = fs.promises;
const { R } = require("redbean-node");
const { setSetting, setting } = require("./util-server");
const { log, sleep, isDev } = require("../src/util");
const { log, sleep, isDevEnv } = require("../src/util");
const knex = require("knex");
const path = require("path");
const { EmbeddedMariaDB } = require("./embedded-mariadb");
@@ -171,7 +171,7 @@ class Database {
* @returns {string} The dev data dir, empty string if not in dev mode or in master branch
*/
static getDevDataDir() {
if (isDev) {
if (isDevEnv()) {
const gitBranch = this.getCurrentGitBranch();
// HEAD means detached head. Don't handle this case, becasuse it is not common.
+12
View File
@@ -13,7 +13,19 @@ import * as utc from "dayjs/plugin/utc";
import jsonata from "jsonata";
/**
* @deprecated Use isDevEnv, because NODE_ENV could be set after this
*/
export const isDev = process.env.NODE_ENV === "development";
/**
* Check if NODE_ENV is development
* @returns yes or no
*/
export function isDevEnv() {
return process.env.NODE_ENV === "development";
}
export const isNode = typeof process !== "undefined" && process?.versions?.node;
/**