ronantakizawa/codereview-bench huggingface dataset:
Repository: louislam/uptime-kuma
File: server/notification-providers/grafana-oncall.js (JavaScript)

Code before:
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");

class GrafanaOncall extends NotificationProvider {

    name = "GrafanaOncall";

    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
        let okMsg = "Sent Successfully.";
        try {
            if (heartbeatJSON["status"] === DOWN) {
                let grafanadowndata = {
                    title: monitorJSON["name"] + " is down",
                    message: heartbeatJSON["msg"],
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanadowndata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === UP) {
                let grafanaupdata = {
                    title: monitorJSON["name"] + " is up",
                    message: heartbeatJSON["msg"],
                    state: "ok",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            }
        } catch (error) {
            this.throwGeneralAxiosError(error);
        }


Review comment (suggestion):
You are missing the testing button and certificate handling

```suggestion
            if (heartbeatJSON === null) {
                let grafanaupdata = {
                    title: "General notification",
                    message: msg,
                    state: "alerting",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === DOWN) {
```

Code after:
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");

class GrafanaOncall extends NotificationProvider {

    name = "GrafanaOncall";

    /**
     * @inheritdoc
     */
    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {

        if (!notification.GrafanaOncallURL) {
            throw new Error("GrafanaOncallURL cannot be empty");
        }

        let okMsg = "Sent Successfully.";
        try {
            if (heartbeatJSON === null) {
                let grafanaupdata = {
                    title: "General notification",
                    message: msg,
                    state: "alerting",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === DOWN) {
                let grafanadowndata = {
                    title: monitorJSON["name"] + " is down",
                    message: heartbeatJSON["msg"],
                    state: "alerting",
                };
                await axios.post(
Repository: louislam/uptime-kuma
File: server/notification-providers/grafana-oncall.js (JavaScript)

Code before:
    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
        let okMsg = "Sent Successfully.";
        try {
            if (heartbeatJSON["status"] === DOWN) {
                let grafanadowndata = {
                    title: monitorJSON["name"] + " is down",
                    message: heartbeatJSON["msg"],
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanadowndata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === UP) {
                let grafanaupdata = {
                    title: monitorJSON["name"] + " is up",
                    message: heartbeatJSON["msg"],
                    state: "ok",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            }
        } catch (error) {
            this.throwGeneralAxiosError(error);
        }

    }
}

module.exports = GrafanaOncall;


Review comment (suggestion):
```suggestion
                    message: heartbeatJSON["msg"],
                    state: "alerting",
```

Code after:
    /**
     * @inheritdoc
     */
    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {

        if (!notification.GrafanaOncallURL) {
            throw new Error("GrafanaOncallURL cannot be empty");
        }

        let okMsg = "Sent Successfully.";
        try {
            if (heartbeatJSON === null) {
                let grafanaupdata = {
                    title: "General notification",
                    message: msg,
                    state: "alerting",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === DOWN) {
                let grafanadowndata = {
                    title: monitorJSON["name"] + " is down",
                    message: heartbeatJSON["msg"],
                    state: "alerting",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanadowndata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === UP) {
                let grafanaupdata = {
                    title: monitorJSON["name"] + " is up",
                    message: heartbeatJSON["msg"],
                    state: "ok",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            }
        } catch (error) {
            this.throwGeneralAxiosError(error);
        }

    }
}
Repository: louislam/uptime-kuma
File: server/notification-providers/grafana-oncall.js (JavaScript)

Code before:
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");

class GrafanaOncall extends NotificationProvider {

    name = "GrafanaOncall";

    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
        let okMsg = "Sent Successfully.";
        try {
            if (heartbeatJSON === null) {
                let grafanaupdata = {
                    title: "General notification",
                    message: msg,
                    state: "alerting",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === DOWN) {
                let grafanadowndata = {
                    title: monitorJSON["name"] + " is down",
                    message: heartbeatJSON["msg"],
                    state: "alerting",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanadowndata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === UP) {
                let grafanaupdata = {
                    title: monitorJSON["name"] + " is up",
                    message: heartbeatJSON["msg"],

Review comment (suggestion):
```suggestion
    /**
     * @inheritdoc
     */
    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
```

Some new eslint requirements have been merged into the version 2 branch. The key one that affects this PR is the requirement for all methods to have JSDoc. In this case, given it is an inherited method, you can just inherit the docs as well.

Code after:
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");

class GrafanaOncall extends NotificationProvider {

    name = "GrafanaOncall";

    /**
     * @inheritdoc
     */
    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {

        if (!notification.GrafanaOncallURL) {
            throw new Error("GrafanaOncallURL cannot be empty");
        }

        let okMsg = "Sent Successfully.";
        try {
            if (heartbeatJSON === null) {
                let grafanaupdata = {
                    title: "General notification",
                    message: msg,
                    state: "alerting",
                };
                await axios.post(
                    notification.GrafanaOncallURL,
                    grafanaupdata
                );
                return okMsg;
            } else if (heartbeatJSON["status"] === DOWN) {
                let grafanadowndata = {
                    title: monitorJSON["name"] + " is down",
                    message: heartbeatJSON["msg"],
                    state: "alerting",
                };
                await axios.post(
Repository: louislam/uptime-kuma
File: server/server.js (JavaScript)

Code before:
    }
}

/** Resume active monitors */
async function startMonitors() {
    let list = await R.find("monitor", " active = 1 ");

    for (let monitor of list) {
        server.monitorList[monitor.id] = monitor;
    }

    for (let monitor of list) {
        await monitor.start(io);
        // Give some delays, so all monitors won't make request at the same moment when just start the server.
        await sleep(getRandomInt(300, 1000));
    }
}

/**
 * Shutdown the application
 * Stops all monitors and closes the database connection.
 * @param {string} signal The signal that triggered this function to be called.
 * @returns {Promise<void>}
 */
async function shutdownFunction(signal) {
    log.info("server", "Shutdown requested");
    log.info("server", "Called signal: " + signal);

    await server.stop();

    log.info("server", "Stopping all monitors");
    for (let id in server.monitorList) {
        let monitor = server.monitorList[id];
        await monitor.stop();
    }
    await sleep(2000);
    await Database.close();

    stopBackgroundJobs();
    await cloudflaredStop();
    Settings.stopCacheCleaner();
}

/** Final function called before application exits */
function finalFunction() {
    log.info("server", "Graceful shutdown successful!");
}

gracefulShutdown(server.httpServer, {
    signals: "SIGINT SIGTERM",
    timeout: 30000,                   // timeout: 30 secs

Review comment (suggestion):
@chakflying Adding `async` to the function is ok, but I am not sure if adding `await` here or other places are ok or not.

For example, one of monitors gets sucked here and the whole application gets sucked by this monitor. Not quite sure.

Code after:
    log.info("manage", `Pause Monitor: ${monitorID} User ID: ${userID}`);

    await R.exec("UPDATE monitor SET active = 0 WHERE id = ? AND user_id = ? ", [
        monitorID,
        userID,
    ]);

    if (monitorID in server.monitorList) {
        await server.monitorList[monitorID].stop();
        server.monitorList[monitorID].active = 0;
    }
}

/**
 * Resume active monitors
 * @returns {Promise<void>}
 */
async function startMonitors() {
    let list = await R.find("monitor", " active = 1 ");

    for (let monitor of list) {
        server.monitorList[monitor.id] = monitor;
    }

    for (let monitor of list) {
        await monitor.start(io);
        // Give some delays, so all monitors won't make request at the same moment when just start the server.
        await sleep(getRandomInt(300, 1000));
    }
}

/**
 * Shutdown the application
 * Stops all monitors and closes the database connection.
 * @param {string} signal The signal that triggered this function to be called.
 * @returns {Promise<void>}
 */
async function shutdownFunction(signal) {
    log.info("server", "Shutdown requested");
    log.info("server", "Called signal: " + signal);

    await server.stop();

    log.info("server", "Stopping all monitors");
    for (let id in server.monitorList) {
        let monitor = server.monitorList[id];
        await monitor.stop();
    }
    await sleep(2000);
    await Database.close();
Repository: louislam/uptime-kuma
File: server/model/monitor.js (JavaScript)

Code before:
     * @returns {boolean} gamedig should only use the provided port
     */
    getGameDigGivenPortOnly() {
        return Boolean(this.gamedigGivenPortOnly);
    }

    /**
     * Parse to boolean
     * @returns {boolean} Kafka Producer Ssl enabled?
     */
    getKafkaProducerSsl() {
        return Boolean(this.kafkaProducerSsl);
    }

    /**
     * Parse to boolean
     * @returns {boolean} Kafka Producer Allow Auto Topic Creation Enabled?
     */
    getKafkaProducerAllowAutoTopicCreation() {
        return Boolean(this.kafkaProducerAllowAutoTopicCreation);
    }

    /**
     * Start monitor
     * @param {Server} io Socket server instance
     * @returns {void}
     */
    async start(io) {
        let previousBeat = null;
        let retries = 0;

        this.prometheus = new Prometheus(this);

        const beat = async () => {

            let beatInterval = this.interval;

            if (! beatInterval) {
                beatInterval = 1;
            }

            if (demoMode) {
                if (beatInterval < 20) {
                    console.log("beat interval too low, reset to 20s");
                    beatInterval = 20;
                }
            }

            // Expose here for prometheus update
            // undefined if not https
            let tlsInfo = undefined;

Review comment (suggestion):
```suggestion
     * @returns {Promise<void>}
```

Code after:
     * @returns {boolean} gamedig should only use the provided port
     */
    getGameDigGivenPortOnly() {
        return Boolean(this.gamedigGivenPortOnly);
    }

    /**
     * Parse to boolean
     * @returns {boolean} Kafka Producer Ssl enabled?
     */
    getKafkaProducerSsl() {
        return Boolean(this.kafkaProducerSsl);
    }

    /**
     * Parse to boolean
     * @returns {boolean} Kafka Producer Allow Auto Topic Creation Enabled?
     */
    getKafkaProducerAllowAutoTopicCreation() {
        return Boolean(this.kafkaProducerAllowAutoTopicCreation);
    }

    /**
     * Start monitor
     * @param {Server} io Socket server instance
     * @returns {Promise<void>}
     */
    async start(io) {
        let previousBeat = null;
        let retries = 0;

        this.prometheus = new Prometheus(this);

        const beat = async () => {

            let beatInterval = this.interval;

            if (! beatInterval) {
                beatInterval = 1;
            }

            if (demoMode) {
                if (beatInterval < 20) {
                    console.log("beat interval too low, reset to 20s");
                    beatInterval = 20;
                }
            }

            // Expose here for prometheus update
            // undefined if not https
            let tlsInfo = undefined;
Repository: louislam/uptime-kuma
File: server/docker.js (JavaScript)

Code before:
const axios = require("axios");
const { R } = require("redbean-node");
const version = require("../package.json").version;
const https = require("https");

class DockerHost {
    /**
     * Save a docker host
     * @param {Object} dockerHost Docker host to save
     * @param {?number} dockerHostID ID of the docker host to update
     * @param {number} userID ID of the user who adds the docker host
     * @returns {Promise<Bean>}
     */
    static async save(dockerHost, dockerHostID, userID) {
        let bean;

        if (dockerHostID) {
            bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]);

            if (!bean) {
                throw new Error("docker host not found");
            }

        } else {
            bean = R.dispense("docker_host");
        }

        bean.user_id = userID;
        bean.docker_daemon = dockerHost.dockerDaemon;
        bean.docker_type = dockerHost.dockerType;
        bean.name = dockerHost.name;

        await R.store(bean);


Review comment (suggestion):
Thanks for the pr.

Since `data/` can be changed by the env var `DATA_DIR`, `data/` cannot be hardcoded here.

But actually, in my opinion, these 4 env vars are not necessary, just hardcode:
 - Database.dataDir + "docker-tls/"
 - ca.pem
 - cert.pem
 - key.pem

Other sub-directories are created in this function.
https://github.com/louislam/uptime-kuma/blob/cdb38d49ebaedbe28ab160ff17e9385826df9893/server/database.js#L93-L108

Code after:
const axios = require("axios");
const { R } = require("redbean-node");
const version = require("../package.json").version;
const https = require("https");
const fs = require("fs");

class DockerHost {

    static CertificateBasePath = process.env.DOCKER_TLS_DIR_PATH || "data/docker-tls/";
    static CertificateFileNameCA = process.env.DOCKER_TLS_FILE_NAME_CA || "ca.pem";
    static CertificateFileNameCert = process.env.DOCKER_TLS_FILE_NAME_CA || "cert.pem";
    static CertificateFileNameKey = process.env.DOCKER_TLS_FILE_NAME_CA || "key.pem";

    /**
     * Save a docker host
     * @param {Object} dockerHost Docker host to save
     * @param {?number} dockerHostID ID of the docker host to update
     * @param {number} userID ID of the user who adds the docker host
     * @returns {Promise<Bean>}
     */
    static async save(dockerHost, dockerHostID, userID) {
        let bean;

        if (dockerHostID) {
            bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]);

            if (!bean) {
                throw new Error("docker host not found");
            }

        } else {
            bean = R.dispense("docker_host");
        }
Repository: louislam/uptime-kuma
File: server/monitor-types/tailscale-ping.js (JavaScript)

Code before:
const { MonitorType } = require("./monitor-type");
const { UP } = require("../../src/util");

class TailscalePing extends MonitorType {

    name = "tailscale-ping";

    async check(monitor, heartbeat) {
        // TODO: Logic here

        // Successful:
        // heartbeat.status = UP;
        // heartbeat.ping = timing.responseEnd;
        // heartbeat.msg = "????"

        // Failed:
        // throw new Error("????");

        throw new Error("Not implemented");
    }
}

module.exports = {
    TailscalePing,
};


Review comment (suggestion):
I am actually changing the structure, so I built the boilerplate for you. 

Basically, you have to work on this `check()` function, see comments for more details and see #3308 for reference.

Code after:
const { MonitorType } = require("./monitor-type");
const { UP, log } = require("../../src/util");
const exec = require("child_process").exec;

/**
 * A TailscalePing class extends the MonitorType.
 * It runs Tailscale ping to monitor the status of a specific node.
 */
class TailscalePing extends MonitorType {

    name = "tailscale-ping";

    /**
     * Checks the ping status of the URL associated with the monitor.
     * It then parses the Tailscale ping command output to update the heatrbeat.
     *
     * @param {Object} monitor - The monitor object associated with the check.
     * @param {Object} heartbeat - The heartbeat object to update.
     * @throws Will throw an error if checking Tailscale ping encounters any error
     */
    async check(monitor, heartbeat) {
        try {
            let tailscaleOutput = await this.runTailscalePing(monitor.hostname, monitor.interval);
            this.parseTailscaleOutput(tailscaleOutput, heartbeat);
        } catch (err) {
            log.debug("Tailscale", err);
            // trigger log function somewhere to display a notification or alert to the user (but how?)
            throw new Error(`Error checking Tailscale ping: ${err}`);
        }
    }

    /**
     * Runs the Tailscale ping command to the given URL.
     *
     * @param {string} hostname - The hostname to ping.
     * @returns {Promise<string>} - A Promise that resolves to the output of the Tailscale ping command
     * @throws Will throw an error if the command execution encounters any error.
     */
    async runTailscalePing(hostname, interval) {
        let cmd = `tailscale ping ${hostname}`;

        log.debug("Tailscale", cmd);

        return new Promise((resolve, reject) => {
            let timeout = interval * 1000 * 0.8;
            exec(cmd, { timeout: timeout }, (error, stdout, stderr) => {
Repository: louislam/uptime-kuma
File: server/monitor-types/tailscale-ping.js (JavaScript)

Code before:
// TODO: Handle Case 5
// In cases where offline peers are not pingable, how should he handle timeout messages? Do we break out of the "ping (ip) timed out" loop and let the server re-call the check function? or do we include the "timed out" string in the expected error messages line and throw the error? (along with "no matching peer" and "is local Tailscale IP")

const { MonitorType } = require("./monitor-type");
const { UP } = require("../../src/util");

// child_process
const exec = require('child_process').exec;

// cases accounted for
// 1 calling tailscale ping to local
// CMD: tailscale ping 100.100.100.1
// 100.100.100.1 is local Tailscale IP
// 2 the normal scenario
// CMD: tailscale ping 100.100.100.2
// pong from (hostname) (100.100.100.2) via [2001:1245:1234:1234::abc]:12345 in 123ms
// 3 ping a nonexistent IP or inaccessible node
// CMD: tailscale ping 100.100.100.3
// no matching peer
// 4 ping a peer that you cannot make a outgoing connection to
// CMD: tailscale ping 100.100.100.4
// multiple 'pong from' lines (pong from hostname (100.100.100.4) via DERP(tok) in 123ms) then ends with a 'direct connection not established' line
// We only care about the first 'pong from' line for the purpose of checking uptime. (this is a good example use of "tailscale ping" becasue traditional ICMP pings would not be able to check the uptime of peers if outgoing connections to that specified peer are blocked)
// 5 pinging a offline peer
// CMD: tailscale ping 100.100.100.5
// multiple 'timed out' lines (ping "100.100.100.5" timed out) then ends with a 'no reply' line
// This needs to be worked on

Review comment (suggestion):
I don't like `TODO` or `FIXME` comments as they likely wont be removed.
=> please choose one implementation and add a comment why you chose this way if complex enough.

Code after:
const { MonitorType } = require("./monitor-type");
const { UP, log } = require("../../src/util");
const exec = require("child_process").exec;

/**
 * A TailscalePing class extends the MonitorType.
 * It runs Tailscale ping to monitor the status of a specific node.
 */
class TailscalePing extends MonitorType {

    name = "tailscale-ping";

    /**
     * Checks the ping status of the URL associated with the monitor.
     * It then parses the Tailscale ping command output to update the heatrbeat.
     *
     * @param {Object} monitor - The monitor object associated with the check.
     * @param {Object} heartbeat - The heartbeat object to update.
     * @throws Will throw an error if checking Tailscale ping encounters any error
     */
    async check(monitor, heartbeat) {
        try {
            let tailscaleOutput = await this.runTailscalePing(monitor.hostname, monitor.interval);
            this.parseTailscaleOutput(tailscaleOutput, heartbeat);
        } catch (err) {
            log.debug("Tailscale", err);
            // trigger log function somewhere to display a notification or alert to the user (but how?)