uptime-kuma/server/database-error-detector.js
2026-01-09 05:11:41 +00:00

80 lines
3.1 KiB
JavaScript

const Database = require("./database");
/**
* Check if an error is specifically from a database operation
* This is more restrictive to avoid false positives (e.g., notification send failures)
* @param {Error} error The error to check
* @returns {boolean} True if this is a database error
*/
function isDatabaseError(error) {
if (!error) {
return false;
}
// Check for MySQL/MariaDB specific error codes (most reliable indicator)
if (
error.code &&
(error.code.startsWith("ER_") || // MySQL/MariaDB error codes
error.code === "PROTOCOL_CONNECTION_LOST" ||
error.code === "PROTOCOL_ENQUEUE_AFTER_QUIT" ||
error.code === "PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR")
) {
return true;
}
// Check if error originated from database operations by examining stack trace
const stack = error.stack || "";
const isFromDatabaseCode =
stack.includes("redbean-node") ||
stack.includes("/server/database.js") ||
(stack.includes("/server/model/") && (stack.includes("R.") || stack.includes("knex")));
// For connection errors, only consider them database errors if:
// 1. They're from database code (stack trace check above), AND
// 2. They match database connection patterns
if (isFromDatabaseCode) {
// Check for connection errors that could be database-related
if (
error.code &&
(error.code === "EHOSTUNREACH" ||
error.code === "ECONNREFUSED" ||
error.code === "ETIMEDOUT" ||
error.code === "ENOTFOUND")
) {
// For MariaDB/MySQL, verify it's connecting to the actual database host/port
if (Database.dbConfig && Database.dbConfig.type && Database.dbConfig.type.endsWith("mariadb")) {
const dbPort = Database.dbConfig.port || 3306;
const dbHost = Database.dbConfig.hostname;
// Only match if port matches database port OR address matches database hostname
if ((error.port && error.port === dbPort) || (error.address && error.address === dbHost)) {
return true;
}
}
// For SQLite, connection errors from database code are database errors
if (Database.dbConfig && Database.dbConfig.type === "sqlite" && error.syscall === "connect") {
return true;
}
}
// Check for database-specific error messages (only if from database code)
if (
error.message &&
(error.message.includes("SQLITE_") ||
error.message.includes("SQLITE_ERROR") ||
error.message.includes("SQLITE_BUSY") ||
error.message.includes("SQLITE_LOCKED") ||
error.message.includes("database is locked") ||
error.message.includes("no such table") ||
(error.message.includes("Connection lost") && stack.includes("mysql")))
) {
return true;
}
}
return false;
}
module.exports = {
isDatabaseError,
};