summaryrefslogtreecommitdiffstats
path: root/extra/reset-password.js
blob: b87d90f16a68f8b6e324a08c9822bd2dc8846f82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
console.log("== Uptime Kuma Reset Password Tool ==");

const Database = require("../server/database");
const { R } = require("redbean-node");
const readline = require("readline");
const { initJWTSecret } = require("../server/util-server");
const User = require("../server/model/user");
const { io } = require("socket.io-client");
const { localWebSocketURL } = require("../server/config");
const args = require("args-parser")(process.argv);

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const main = async () => {
    if ("dry-run" in args) {
        console.log("Dry run mode, no changes will be made.");
    }

    console.log("Connecting the database");

    try {
        Database.initDataDir(args);
        await Database.connect(false, false, true);
        // No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
        if (!process.env.TEST_BACKEND) {
            const user = await R.findOne("user");
            if (! user) {
                throw new Error("user not found, have you installed?");
            }

            console.log("Found user: " + user.username);

            while (true) {
                let password;
                let confirmPassword;

                // When called with "--new-password" argument for unattended modification (e.g. npm run reset-password -- --new_password=secret)
                if ("new-password" in args) {
                    console.log("Using password from argument");
                    console.warn("\x1b[31m%s\x1b[0m", "Warning: the password might be stored, in plain text, in your shell's history");
                    password = confirmPassword = args["new-password"] + "";
                } else {
                    password = await question("New Password: ");
                    confirmPassword = await question("Confirm New Password: ");
                }

                if (password === confirmPassword) {
                    if (!("dry-run" in args)) {
                        await User.resetPassword(user.id, password);

                        // Reset all sessions by reset jwt secret
                        await initJWTSecret();

                        // Disconnect all other socket clients of the user
                        await disconnectAllSocketClients(user.username, password);
                    }
                    break;
                } else {
                    console.log("Passwords do not match, please try again.");
                }
            }
            console.log("Password reset successfully.");

        }
    } catch (e) {
        console.error("Error: " + e.message);
    }

    await Database.close();
    rl.close();

    console.log("Finished.");
};

/**
 * Ask question of user
 * @param {string} question Question to ask
 * @returns {Promise<string>} Users response
 */
function question(question) {
    return new Promise((resolve) => {
        rl.question(question, (answer) => {
            resolve(answer);
        });
    });
}

/**
 * Disconnect all socket clients of the user
 * @param {string} username Username
 * @param {string} password Password
 * @returns {Promise<void>} Promise
 */
function disconnectAllSocketClients(username, password) {
    return new Promise((resolve) => {
        console.log("Connecting to " + localWebSocketURL + " to disconnect all other socket clients");

        // Disconnect all socket connections
        const socket = io(localWebSocketURL, {
            reconnection: false,
            timeout: 5000,
        });
        socket.on("connect", () => {
            socket.emit("login", {
                username,
                password,
            }, (res) => {
                if (res.ok) {
                    console.log("Logged in.");
                    socket.emit("disconnectOtherSocketClients");
                } else {
                    console.warn("Login failed.");
                    console.warn("Please restart the server to disconnect all sessions.");
                }
                socket.close();
            });
        });

        socket.on("connect_error", function () {
            // The localWebSocketURL is not guaranteed to be working for some complicated Uptime Kuma setup
            // Ask the user to restart the server manually
            console.warn("Failed to connect to " + localWebSocketURL);
            console.warn("Please restart the server to disconnect all sessions manually.");
            resolve();
        });
        socket.on("disconnect", () => {
            resolve();
        });
    });
}

if (!process.env.TEST_BACKEND) {
    main();
}

module.exports = {
    main,
};