uptime-kuma/server/embedded-mariadb.js

177 lines
4.9 KiB
JavaScript
Raw Normal View History

2022-12-23 14:43:56 +00:00
const { log } = require("../src/util");
const childProcess = require("child_process");
2023-02-05 09:45:36 +00:00
const fs = require("fs");
2023-02-05 10:01:54 +00:00
const mysql = require("mysql2");
2022-12-23 14:43:56 +00:00
2023-02-05 09:45:36 +00:00
/**
* It is only used inside the docker container
*/
2022-12-23 14:43:56 +00:00
class EmbeddedMariaDB {
2023-02-05 09:45:36 +00:00
static instance = null;
2022-12-23 14:43:56 +00:00
2023-02-05 09:45:36 +00:00
exec = "mariadbd";
2022-12-23 14:43:56 +00:00
2023-02-05 09:45:36 +00:00
mariadbDataDir = "/app/data/mariadb";
runDir = "/app/data/run/mariadb";
socketPath = this.runDir + "/mysqld.sock";
/**
* @type {ChildProcessWithoutNullStreams}
* @private
*/
2023-02-05 09:45:36 +00:00
childProcess = null;
running = false;
started = false;
/**
* @returns {EmbeddedMariaDB} The singleton instance
2023-02-05 09:45:36 +00:00
*/
static getInstance() {
if (!EmbeddedMariaDB.instance) {
EmbeddedMariaDB.instance = new EmbeddedMariaDB();
}
return EmbeddedMariaDB.instance;
2022-12-23 14:43:56 +00:00
}
/**
* @returns {boolean} If the singleton instance is created
*/
2023-02-05 09:45:36 +00:00
static hasInstance() {
return !!EmbeddedMariaDB.instance;
}
/**
* Start the embedded MariaDB
* @returns {Promise<void>|void} A promise that resolves when the MariaDB is started or void if it is already started
2023-02-05 09:45:36 +00:00
*/
start() {
2022-12-23 14:43:56 +00:00
if (this.childProcess) {
2023-02-05 09:45:36 +00:00
log.info("mariadb", "Already started");
2022-12-23 14:43:56 +00:00
return;
}
2023-02-05 10:01:54 +00:00
this.initDB();
2023-02-05 09:45:36 +00:00
2022-12-23 14:43:56 +00:00
this.running = true;
2023-02-05 09:45:36 +00:00
log.info("mariadb", "Starting Embedded MariaDB");
this.childProcess = childProcess.spawn(this.exec, [
"--user=node",
"--datadir=" + this.mariadbDataDir,
`--socket=${this.socketPath}`,
`--pid-file=${this.runDir}/mysqld.pid`,
]);
2022-12-23 14:43:56 +00:00
this.childProcess.on("close", (code) => {
this.running = false;
this.childProcess = null;
2023-02-05 09:45:36 +00:00
this.started = false;
log.info("mariadb", "Stopped Embedded MariaDB: " + code);
if (code !== 0) {
log.info("mariadb", "Try to restart Embedded MariaDB as it is not stopped by user");
this.start();
}
2022-12-23 14:43:56 +00:00
});
this.childProcess.on("error", (err) => {
if (err.code === "ENOENT") {
2023-02-05 09:45:36 +00:00
log.error("mariadb", `Embedded MariaDB: ${this.exec} is not found`);
2022-12-23 14:43:56 +00:00
} else {
2023-02-05 09:45:36 +00:00
log.error("mariadb", err);
2022-12-23 14:43:56 +00:00
}
});
2023-02-05 09:45:36 +00:00
let handler = (data) => {
log.debug("mariadb", data.toString("utf-8"));
if (data.toString("utf-8").includes("ready for connections")) {
2023-02-05 10:01:54 +00:00
this.initDBAfterStarted();
2023-02-05 09:45:36 +00:00
}
};
this.childProcess.stdout.on("data", handler);
this.childProcess.stderr.on("data", handler);
return new Promise((resolve) => {
let interval = setInterval(() => {
if (this.started) {
clearInterval(interval);
resolve();
} else {
log.info("mariadb", "Waiting for Embedded MariaDB to start...");
}
}, 1000);
2022-12-23 14:43:56 +00:00
});
}
/**
* Stop all the child processes
* @returns {void}
*/
2023-02-05 09:45:36 +00:00
stop() {
2022-12-23 14:43:56 +00:00
if (this.childProcess) {
this.childProcess.kill("SIGINT");
this.childProcess = null;
}
}
/**
* Install MariaDB if it is not installed and make sure the `runDir` directory exists
* @returns {void}
*/
2023-02-05 10:01:54 +00:00
initDB() {
if (!fs.existsSync(this.mariadbDataDir)) {
log.info("mariadb", `Embedded MariaDB: ${this.mariadbDataDir} is not found, create one now.`);
fs.mkdirSync(this.mariadbDataDir, {
recursive: true,
});
let result = childProcess.spawnSync("mysql_install_db", [
"--user=node",
"--ldata=" + this.mariadbDataDir,
]);
if (result.status !== 0) {
let error = result.stderr.toString("utf-8");
log.error("mariadb", error);
return;
} else {
log.info("mariadb", "Embedded MariaDB: mysql_install_db done:" + result.stdout.toString("utf-8"));
}
}
if (!fs.existsSync(this.runDir)) {
log.info("mariadb", `Embedded MariaDB: ${this.runDir} is not found, create one now.`);
fs.mkdirSync(this.runDir, {
recursive: true,
});
}
}
/**
* Initialise the "kuma" database in mariadb if it does not exist
* @returns {Promise<void>}
*/
2023-02-05 10:01:54 +00:00
async initDBAfterStarted() {
const connection = mysql.createConnection({
socketPath: this.socketPath,
user: "node",
});
let result = await connection.execute("CREATE DATABASE IF NOT EXISTS `kuma`");
log.debug("mariadb", "CREATE DATABASE: " + JSON.stringify(result));
log.info("mariadb", "Embedded MariaDB is ready for connections");
this.started = true;
}
2022-12-23 14:43:56 +00:00
}
2023-02-05 09:45:36 +00:00
module.exports = {
EmbeddedMariaDB,
};