mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-23 14:54:05 +00:00
Compare commits
9 commits
7be040472a
...
986b5979b9
Author | SHA1 | Date | |
---|---|---|---|
|
986b5979b9 | ||
|
8a432ac937 | ||
|
778363a948 | ||
|
6899603eb7 | ||
|
13ea190298 | ||
|
a7407a1b65 | ||
|
5bcde56a0f | ||
|
5864c6dd88 | ||
|
595b35fb15 |
12 changed files with 139 additions and 70 deletions
7
db/knex_migrations/2024-10-31-0000-fix-snmp-monitor.js
Normal file
7
db/knex_migrations/2024-10-31-0000-fix-snmp-monitor.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
exports.up = function (knex) {
|
||||
return knex("monitor").whereNull("json_path_operator").update("json_path_operator", "==");
|
||||
};
|
||||
exports.down = function (knex) {
|
||||
// changing the json_path_operator back to null for all "==" is not possible anymore
|
||||
// we have lost the context which fields have been set explicitely in >= v2.0 and which would need to be reverted
|
||||
};
|
|
@ -6,14 +6,13 @@ import {
|
|||
checkDocker,
|
||||
checkTagExists,
|
||||
checkVersionFormat,
|
||||
dryRun,
|
||||
getRepoName,
|
||||
getRepoNames,
|
||||
pressAnyKey,
|
||||
execSync, uploadArtifacts,
|
||||
} from "./lib.mjs";
|
||||
import semver from "semver";
|
||||
|
||||
const repoName = getRepoName();
|
||||
const repoNames = getRepoNames();
|
||||
const version = process.env.RELEASE_BETA_VERSION;
|
||||
const githubToken = process.env.RELEASE_GITHUB_TOKEN;
|
||||
|
||||
|
@ -39,7 +38,7 @@ if (semverIdentifier[0] !== "beta") {
|
|||
checkDocker();
|
||||
|
||||
// Check if the tag exists
|
||||
await checkTagExists(repoName, version);
|
||||
await checkTagExists(repoNames, version);
|
||||
|
||||
// node extra/beta/update-version.js
|
||||
execSync("node ./extra/beta/update-version.js");
|
||||
|
@ -48,16 +47,16 @@ execSync("node ./extra/beta/update-version.js");
|
|||
buildDist();
|
||||
|
||||
// Build slim image (rootless)
|
||||
buildImage(repoName, [ "beta-slim-rootless", ver(version, "slim-rootless") ], "rootless", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
buildImage(repoNames, [ "beta-slim-rootless", ver(version, "slim-rootless") ], "rootless", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
|
||||
// Build full image (rootless)
|
||||
buildImage(repoName, [ "beta-rootless", ver(version, "rootless") ], "rootless");
|
||||
buildImage(repoNames, [ "beta-rootless", ver(version, "rootless") ], "rootless");
|
||||
|
||||
// Build slim image
|
||||
buildImage(repoName, [ "beta-slim", ver(version, "slim") ], "release", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
buildImage(repoNames, [ "beta-slim", ver(version, "slim") ], "release", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
|
||||
// Build full image
|
||||
buildImage(repoName, [ "beta", version ], "release");
|
||||
buildImage(repoNames, [ "beta", version ], "release");
|
||||
|
||||
await pressAnyKey();
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ import {
|
|||
checkDocker,
|
||||
checkTagExists,
|
||||
checkVersionFormat,
|
||||
getRepoName,
|
||||
getRepoNames,
|
||||
pressAnyKey, execSync, uploadArtifacts
|
||||
} from "./lib.mjs";
|
||||
|
||||
const repoName = getRepoName();
|
||||
const repoNames = getRepoNames();
|
||||
const version = process.env.RELEASE_VERSION;
|
||||
const githubToken = process.env.RELEASE_GITHUB_TOKEN;
|
||||
|
||||
|
@ -28,7 +28,7 @@ checkVersionFormat(version);
|
|||
checkDocker();
|
||||
|
||||
// Check if the tag exists
|
||||
await checkTagExists(repoName, version);
|
||||
await checkTagExists(repoNames, version);
|
||||
|
||||
// node extra/beta/update-version.js
|
||||
execSync("node extra/update-version.js");
|
||||
|
@ -37,16 +37,16 @@ execSync("node extra/update-version.js");
|
|||
buildDist();
|
||||
|
||||
// Build slim image (rootless)
|
||||
buildImage(repoName, [ "2-slim-rootless", ver(version, "slim-rootless") ], "rootless", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
buildImage(repoNames, [ "2-slim-rootless", ver(version, "slim-rootless") ], "rootless", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
|
||||
// Build full image (rootless)
|
||||
buildImage(repoName, [ "2-rootless", ver(version, "rootless") ], "rootless");
|
||||
buildImage(repoNames, [ "2-rootless", ver(version, "rootless") ], "rootless");
|
||||
|
||||
// Build slim image
|
||||
buildImage(repoName, [ "next-slim", "2-slim", ver(version, "slim") ], "release", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
buildImage(repoNames, [ "next-slim", "2-slim", ver(version, "slim") ], "release", "BASE_IMAGE=louislam/uptime-kuma:base2-slim");
|
||||
|
||||
// Build full image
|
||||
buildImage(repoName, [ "next", "2", version ], "release");
|
||||
buildImage(repoNames, [ "next", "2", version ], "release");
|
||||
|
||||
await pressAnyKey();
|
||||
|
||||
|
|
|
@ -24,8 +24,15 @@ export function checkDocker() {
|
|||
/**
|
||||
* Get Docker Hub repository name
|
||||
*/
|
||||
export function getRepoName() {
|
||||
return process.env.RELEASE_REPO_NAME || "louislam/uptime-kuma";
|
||||
export function getRepoNames() {
|
||||
if (process.env.RELEASE_REPO_NAMES) {
|
||||
// Split by comma
|
||||
return process.env.RELEASE_REPO_NAMES.split(",").map((name) => name.trim());
|
||||
}
|
||||
return [
|
||||
"louislam/uptime-kuma",
|
||||
"ghcr.io/louislam/uptime-kuma",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,7 +49,7 @@ export function buildDist() {
|
|||
|
||||
/**
|
||||
* Build docker image and push to Docker Hub
|
||||
* @param {string} repoName Docker Hub repository name
|
||||
* @param {string[]} repoNames Docker Hub repository names
|
||||
* @param {string[]} tags Docker image tags
|
||||
* @param {string} target Dockerfile's target name
|
||||
* @param {string} buildArgs Docker build args
|
||||
|
@ -50,7 +57,7 @@ export function buildDist() {
|
|||
* @param {string} platform Build platform
|
||||
* @returns {void}
|
||||
*/
|
||||
export function buildImage(repoName, tags, target, buildArgs = "", dockerfile = "docker/dockerfile", platform = "linux/amd64,linux/arm64,linux/arm/v7") {
|
||||
export function buildImage(repoNames, tags, target, buildArgs = "", dockerfile = "docker/dockerfile", platform = "linux/amd64,linux/arm64,linux/arm/v7") {
|
||||
let args = [
|
||||
"buildx",
|
||||
"build",
|
||||
|
@ -60,10 +67,12 @@ export function buildImage(repoName, tags, target, buildArgs = "", dockerfile =
|
|||
platform,
|
||||
];
|
||||
|
||||
for (let repoName of repoNames) {
|
||||
// Add tags
|
||||
for (let tag of tags) {
|
||||
args.push("-t", `${repoName}:${tag}`);
|
||||
}
|
||||
}
|
||||
|
||||
args = [
|
||||
...args,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { buildDist, buildImage, checkDocker, getRepoName } from "./lib.mjs";
|
||||
import { buildDist, buildImage, checkDocker, getRepoNames } from "./lib.mjs";
|
||||
|
||||
// Docker Hub repository name
|
||||
const repoName = getRepoName();
|
||||
const repoNames = getRepoNames();
|
||||
|
||||
// Check if docker is running
|
||||
checkDocker();
|
||||
|
@ -10,7 +10,7 @@ checkDocker();
|
|||
buildDist();
|
||||
|
||||
// Build full image (rootless)
|
||||
buildImage(repoName, [ "nightly2-rootless" ], "nightly-rootless");
|
||||
buildImage(repoNames, [ "nightly2-rootless" ], "nightly-rootless");
|
||||
|
||||
// Build full image
|
||||
buildImage(repoName, [ "nightly2" ], "nightly");
|
||||
buildImage(repoNames, [ "nightly2" ], "nightly");
|
||||
|
|
|
@ -296,7 +296,7 @@ class Database {
|
|||
client: "mysql2",
|
||||
connection: {
|
||||
socketPath: embeddedMariaDB.socketPath,
|
||||
user: "node",
|
||||
user: embeddedMariaDB.username,
|
||||
database: "kuma",
|
||||
timezone: "Z",
|
||||
typeCast: function (field, next) {
|
||||
|
|
|
@ -14,9 +14,15 @@ class EmbeddedMariaDB {
|
|||
|
||||
mariadbDataDir = "/app/data/mariadb";
|
||||
|
||||
runDir = "/app/data/run/mariadb";
|
||||
runDir = "/app/data/run";
|
||||
|
||||
socketPath = this.runDir + "/mysqld.sock";
|
||||
socketPath = this.runDir + "/mariadb.sock";
|
||||
|
||||
/**
|
||||
* The username to connect to the MariaDB
|
||||
* @type {string}
|
||||
*/
|
||||
username = null;
|
||||
|
||||
/**
|
||||
* @type {ChildProcessWithoutNullStreams}
|
||||
|
@ -46,16 +52,42 @@ class EmbeddedMariaDB {
|
|||
|
||||
/**
|
||||
* Start the embedded MariaDB
|
||||
* @throws {Error} If the current user is not "node" or "root"
|
||||
* @returns {Promise<void>|void} A promise that resolves when the MariaDB is started or void if it is already started
|
||||
*/
|
||||
start() {
|
||||
// Check if the current user is "node" or "root"
|
||||
this.username = require("os").userInfo().username;
|
||||
if (this.username !== "node" && this.username !== "root") {
|
||||
throw new Error("Embedded Mariadb supports only 'node' or 'root' user, but the current user is: " + this.username);
|
||||
}
|
||||
|
||||
this.initDB();
|
||||
|
||||
this.startChildProcess();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let interval = setInterval(() => {
|
||||
if (this.started) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
} else {
|
||||
log.info("mariadb", "Waiting for Embedded MariaDB to start...");
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the child process
|
||||
* @returns {void}
|
||||
*/
|
||||
startChildProcess() {
|
||||
if (this.childProcess) {
|
||||
log.info("mariadb", "Already started");
|
||||
return;
|
||||
}
|
||||
|
||||
this.initDB();
|
||||
|
||||
this.running = true;
|
||||
log.info("mariadb", "Starting Embedded MariaDB");
|
||||
this.childProcess = childProcess.spawn(this.exec, [
|
||||
|
@ -63,6 +95,8 @@ class EmbeddedMariaDB {
|
|||
"--datadir=" + this.mariadbDataDir,
|
||||
`--socket=${this.socketPath}`,
|
||||
`--pid-file=${this.runDir}/mysqld.pid`,
|
||||
// Don't add the following option, the mariadb will not report message to the console, which affects initDBAfterStarted()
|
||||
// "--log-error=" + `${this.mariadbDataDir}/mariadb-error.log`,
|
||||
]);
|
||||
|
||||
this.childProcess.on("close", (code) => {
|
||||
|
@ -72,8 +106,8 @@ class EmbeddedMariaDB {
|
|||
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();
|
||||
log.error("mariadb", "Try to restart Embedded MariaDB as it is not stopped by user");
|
||||
this.startChildProcess();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -86,7 +120,7 @@ class EmbeddedMariaDB {
|
|||
});
|
||||
|
||||
let handler = (data) => {
|
||||
log.debug("mariadb", data.toString("utf-8"));
|
||||
log.info("mariadb", data.toString("utf-8"));
|
||||
if (data.toString("utf-8").includes("ready for connections")) {
|
||||
this.initDBAfterStarted();
|
||||
}
|
||||
|
@ -94,17 +128,6 @@ class EmbeddedMariaDB {
|
|||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,9 +152,11 @@ class EmbeddedMariaDB {
|
|||
recursive: true,
|
||||
});
|
||||
|
||||
let result = childProcess.spawnSync("mysql_install_db", [
|
||||
let result = childProcess.spawnSync("mariadb-install-db", [
|
||||
"--user=node",
|
||||
"--ldata=" + this.mariadbDataDir,
|
||||
"--auth-root-socket-user=node",
|
||||
"--datadir=" + this.mariadbDataDir,
|
||||
"--auth-root-authentication-method=socket",
|
||||
]);
|
||||
|
||||
if (result.status !== 0) {
|
||||
|
@ -143,6 +168,17 @@ class EmbeddedMariaDB {
|
|||
}
|
||||
}
|
||||
|
||||
// Check the owner of the mariadb directory, and change it if necessary
|
||||
let stat = fs.statSync(this.mariadbDataDir);
|
||||
if (stat.uid !== 1000 || stat.gid !== 1000) {
|
||||
fs.chownSync(this.mariadbDataDir, 1000, 1000);
|
||||
}
|
||||
|
||||
// Check the permission of the mariadb directory, and change it if it is not 755
|
||||
if (stat.mode !== 0o755) {
|
||||
fs.chmodSync(this.mariadbDataDir, 0o755);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(this.runDir)) {
|
||||
log.info("mariadb", `Embedded MariaDB: ${this.runDir} is not found, create one now.`);
|
||||
fs.mkdirSync(this.runDir, {
|
||||
|
@ -150,6 +186,13 @@ class EmbeddedMariaDB {
|
|||
});
|
||||
}
|
||||
|
||||
stat = fs.statSync(this.runDir);
|
||||
if (stat.uid !== 1000 || stat.gid !== 1000) {
|
||||
fs.chownSync(this.runDir, 1000, 1000);
|
||||
}
|
||||
if (stat.mode !== 0o755) {
|
||||
fs.chmodSync(this.runDir, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +202,7 @@ class EmbeddedMariaDB {
|
|||
async initDBAfterStarted() {
|
||||
const connection = mysql.createConnection({
|
||||
socketPath: this.socketPath,
|
||||
user: "node",
|
||||
user: this.username,
|
||||
});
|
||||
|
||||
let result = await connection.execute("CREATE DATABASE IF NOT EXISTS `kuma`");
|
||||
|
|
|
@ -1522,7 +1522,7 @@ class Monitor extends BeanModel {
|
|||
*/
|
||||
static async getMonitorTag(monitorIDs) {
|
||||
return await R.getAll(`
|
||||
SELECT monitor_tag.monitor_id, monitor_tag.tag_id, tag.name, tag.color
|
||||
SELECT monitor_tag.monitor_id, monitor_tag.tag_id, monitor_tag.value, tag.name, tag.color
|
||||
FROM monitor_tag
|
||||
JOIN tag ON monitor_tag.tag_id = tag.id
|
||||
WHERE monitor_tag.monitor_id IN (${monitorIDs.map((_) => "?").join(",")})
|
||||
|
@ -1567,6 +1567,8 @@ class Monitor extends BeanModel {
|
|||
}
|
||||
tagsMap.get(row.monitor_id).push({
|
||||
tag_id: row.tag_id,
|
||||
monitor_id: row.monitor_id,
|
||||
value: row.value,
|
||||
name: row.name,
|
||||
color: row.color
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { setSettings, setting } = require("../util-server");
|
||||
const { getMonitorRelativeURL, UP } = require("../../src/util");
|
||||
const { getMonitorRelativeURL, UP, log } = require("../../src/util");
|
||||
|
||||
class Slack extends NotificationProvider {
|
||||
name = "slack";
|
||||
|
@ -50,6 +50,7 @@ class Slack extends NotificationProvider {
|
|||
|
||||
const address = this.extractAddress(monitorJSON);
|
||||
if (address) {
|
||||
try {
|
||||
actions.push({
|
||||
"type": "button",
|
||||
"text": {
|
||||
|
@ -57,8 +58,12 @@ class Slack extends NotificationProvider {
|
|||
"text": "Visit site",
|
||||
},
|
||||
"value": "Site",
|
||||
"url": address,
|
||||
"url": new URL(address),
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
log.debug("slack", `Failed to parse address ${address} as URL`);
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
|
|
|
@ -220,6 +220,9 @@ module.exports.statusPageSocketHandler = (socket) => {
|
|||
|
||||
// Delete groups that are not in the list
|
||||
log.debug("socket", "Delete groups that are not in the list");
|
||||
if (groupIDList.length === 0) {
|
||||
await R.exec("DELETE FROM `group` WHERE status_page_id = ?", [ statusPage.id ]);
|
||||
} else {
|
||||
const slots = groupIDList.map(() => "?").join(",");
|
||||
|
||||
const data = [
|
||||
|
@ -227,6 +230,7 @@ module.exports.statusPageSocketHandler = (socket) => {
|
|||
statusPage.id
|
||||
];
|
||||
await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots}) AND status_page_id = ?`, data);
|
||||
}
|
||||
|
||||
const server = UptimeKumaServer.getInstance();
|
||||
|
||||
|
|
|
@ -5,20 +5,20 @@
|
|||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sendgrid-from-email" class="form-label">{{ $t("From Email") }}</label>
|
||||
<input id="sendgrid-from-email" v-model="$parent.notification.sendgridFromEmail" type="email" class="form-control" required>
|
||||
<input id="sendgrid-from-email" v-model="$parent.notification.sendgridFromEmail" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sendgrid-to-email" class="form-label">{{ $t("To Email") }}</label>
|
||||
<input id="sendgrid-to-email" v-model="$parent.notification.sendgridToEmail" type="email" class="form-control" required>
|
||||
<input id="sendgrid-to-email" v-model="$parent.notification.sendgridToEmail" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sendgrid-cc-email" class="form-label">{{ $t("smtpCC") }}</label>
|
||||
<input id="sendgrid-cc-email" v-model="$parent.notification.sendgridCcEmail" type="email" class="form-control">
|
||||
<input id="sendgrid-cc-email" v-model="$parent.notification.sendgridCcEmail" type="text" class="form-control">
|
||||
<div class="form-text">{{ $t("Separate multiple email addresses with commas") }}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sendgrid-bcc-email" class="form-label">{{ $t("smtpBCC") }}</label>
|
||||
<input id="sendgrid-bcc-email" v-model="$parent.notification.sendgridBccEmail" type="email" class="form-control">
|
||||
<input id="sendgrid-bcc-email" v-model="$parent.notification.sendgridBccEmail" type="text" class="form-control">
|
||||
<small class="form-text text-muted">{{ $t("Separate multiple email addresses with commas") }}</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
|
|
|
@ -1065,7 +1065,7 @@ import { hostNameRegexPattern } from "../util-frontend";
|
|||
import HiddenInput from "../components/HiddenInput.vue";
|
||||
import EditMonitorConditions from "../components/EditMonitorConditions.vue";
|
||||
|
||||
const toast = useToast;
|
||||
const toast = useToast();
|
||||
|
||||
const pushTokenLength = 32;
|
||||
|
||||
|
|
Loading…
Reference in a new issue