Compare commits

..

20 Commits

Author SHA1 Message Date
Louis Lam
4d16575599 Merge branch 'master' into 1.23.X-to-2
# Conflicts:
#	package-lock.json
#	package.json
2024-12-20 15:38:45 +08:00
Louis Lam
5bb329fa0e Update to 1.23.16 2024-12-20 15:15:52 +08:00
Louis Lam
09dedc07fb [1.23.X] Update dependencies (#5455) 2024-12-20 15:11:24 +08:00
Louis Lam
6cfae01a0d Merge commit from fork
* [V1 Only] Change dev server's data path to ./data/v1

* Fix GHSA-2qgm-m29m-cj2h
2024-12-20 15:02:22 +08:00
Cyril59310
c0fe669cd8 Fix the save bar of the maintenance menu (#5385)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-11-27 22:09:07 +01:00
Luke Thomas
cdb8ad321d fix: slug input (#5239)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-11-26 01:56:23 +01:00
Ionys
4228dd0a29 Fix the timeout value (#5352) 2024-11-19 01:29:35 +01:00
Ionys
8a432ac937 fix(status page): Make sure the group deletion is correctly handled when groupIDList is empty (#5340) 2024-11-12 19:00:09 +01:00
Dorence Deng
778363a948 fix(notification): #5323 fix EditMonitor.vue useToast error (#5333) 2024-11-11 11:08:43 +01:00
Abhigyan Mohanta
6899603eb7 fix #5314 Email Address Friendly Name Error (#5318) 2024-11-09 23:30:08 +08:00
Daan Meijer
13ea190298 only allow for valid urls in slack button (#5312) 2024-11-05 20:50:47 +01:00
Frank Elsinga
a7407a1b65 fix not setting a default for json monitor operations (#5295) 2024-11-05 20:35:42 +08:00
Louis Lam
5bcde56a0f Publish docker images to ghcr.io (#5311) 2024-11-05 20:26:26 +08:00
Louis Lam
5864c6dd88 Fix: Embedded MariaDB startup issue (#5283) 2024-11-05 20:25:08 +08:00
Ionys
595b35fb15 fix(tags): Fix the tag deletion (#5298) 2024-11-02 13:09:23 +01:00
Louis Lam
0254e72177 Fix: Upload Artifact issue (#5271) 2024-10-29 22:10:05 +08:00
Louis Lam
06a272c119 Update to 2.0.0-beta.0 2024-10-29 21:20:32 +08:00
Louis Lam
93cf63cb06 Fix release script 2024-10-29 21:20:32 +08:00
Louis Lam
32dc76a085 Update to 1.23.15 2024-09-30 05:44:32 +08:00
Louis Lam
c6d6061a9f Pin cheerio to avoid the breaking change of undici (#5142) 2024-09-30 05:41:31 +08:00
19 changed files with 220 additions and 86 deletions

View 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
};

View File

@@ -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,18 +47,18 @@ 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();
// npm run upload-artifacts
uploadArtifacts();
uploadArtifacts(version, githubToken);

View File

@@ -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,21 +37,21 @@ 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();
// npm run upload-artifacts
uploadArtifacts();
uploadArtifacts(version, githubToken);
// node extra/update-wiki-version.js
execSync("node extra/update-wiki-version.js");

View File

@@ -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,9 +67,11 @@ export function buildImage(repoName, tags, target, buildArgs = "", dockerfile =
platform,
];
// Add tags
for (let tag of tags) {
args.push("-t", `${repoName}:${tag}`);
for (let repoName of repoNames) {
// Add tags
for (let tag of tags) {
args.push("-t", `${repoName}:${tag}`);
}
}
args = [
@@ -171,10 +180,43 @@ export function ver(version, identifier) {
/**
* Upload artifacts to GitHub
* docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain
* @param {string} version Version
* @param {string} githubToken GitHub token
* @returns {void}
*/
export function uploadArtifacts() {
execSync("npm run upload-artifacts");
export function uploadArtifacts(version, githubToken) {
let args = [
"buildx",
"build",
"-f",
"docker/dockerfile",
"--platform",
"linux/amd64",
"-t",
"louislam/uptime-kuma:upload-artifact",
"--build-arg",
`VERSION=${version}`,
"--build-arg",
"GITHUB_TOKEN",
"--target",
"upload-artifact",
".",
"--progress",
"plain",
];
if (!dryRun) {
childProcess.spawnSync("docker", args, {
stdio: "inherit",
env: {
...process.env,
GITHUB_TOKEN: githubToken,
},
});
} else {
console.log(`[DRY RUN] docker ${args.join(" ")}`);
}
}
/**

View File

@@ -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");

View File

@@ -0,0 +1,6 @@
import { uploadArtifacts } from "./lib.mjs";
const version = process.env.RELEASE_BETA_VERSION;
const githubToken = process.env.RELEASE_GITHUB_TOKEN;
uploadArtifacts(version, githubToken);

View File

@@ -0,0 +1,6 @@
import { uploadArtifacts } from "./lib.mjs";
const version = process.env.RELEASE_VERSION;
const githubToken = process.env.RELEASE_GITHUB_TOKEN;
uploadArtifacts(version, githubToken);

View File

@@ -39,7 +39,8 @@
"build-docker-builder-go": "docker buildx build -f docker/builder-go.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:builder-go . --push",
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"upload-artifacts": "node extra/release/upload-artifacts.mjs",
"upload-artifacts-beta": "node extra/release/upload-artifacts-beta.mjs",
"setup": "git checkout 1.23.15 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",

View File

@@ -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) {

View File

@@ -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`");

View File

@@ -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
});

View File

@@ -240,6 +240,14 @@ class RealBrowserMonitorType extends MonitorType {
const context = await browser.newContext();
const page = await context.newPage();
// Prevent Local File Inclusion
// Accept only http:// and https://
// https://github.com/louislam/uptime-kuma/security/advisories/GHSA-2qgm-m29m-cj2h
let url = new URL(monitor.url);
if (url.protocol !== "http:" && url.protocol !== "https:") {
throw new Error("Invalid url protocol, only http and https are allowed.");
}
const res = await page.goto(monitor.url, {
waitUntil: "networkidle",
timeout: monitor.interval * 1000 * 0.8,

View File

@@ -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,15 +50,20 @@ class Slack extends NotificationProvider {
const address = this.extractAddress(monitorJSON);
if (address) {
actions.push({
"type": "button",
"text": {
"type": "plain_text",
"text": "Visit site",
},
"value": "Site",
"url": address,
});
try {
actions.push({
"type": "button",
"text": {
"type": "plain_text",
"text": "Visit site",
},
"value": "Site",
"url": new URL(address),
});
} catch (e) {
log.debug("slack", `Failed to parse address ${address} as URL`);
}
}
return actions;

View File

@@ -15,11 +15,13 @@ const server = UptimeKumaServer.getInstance();
router.get("/status/:slug", cache("5 minutes"), async (request, response) => {
let slug = request.params.slug;
slug = slug.toLowerCase();
await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
});
router.get("/status/:slug/rss", cache("5 minutes"), async (request, response) => {
let slug = request.params.slug;
slug = slug.toLowerCase();
await StatusPage.handleStatusPageRSSResponse(response, slug);
});
@@ -37,6 +39,7 @@ router.get("/status-page", cache("5 minutes"), async (request, response) => {
router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => {
allowDevAllOrigin(response);
let slug = request.params.slug;
slug = slug.toLowerCase();
try {
// Get Status Page
@@ -69,6 +72,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
let uptimeList = {};
let slug = request.params.slug;
slug = slug.toLowerCase();
let statusPageID = await StatusPage.slugToID(slug);
let monitorIDList = await R.getCol(`
@@ -111,6 +115,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async (request, response) => {
allowDevAllOrigin(response);
let slug = request.params.slug;
slug = slug.toLowerCase();
try {
// Get Status Page
@@ -145,7 +150,8 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
// overall status-page status badge
router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, response) => {
allowDevAllOrigin(response);
const slug = request.params.slug;
let slug = request.params.slug;
slug = slug.toLowerCase();
const statusPageID = await StatusPage.slugToID(slug);
const {
label,

View File

@@ -220,13 +220,17 @@ module.exports.statusPageSocketHandler = (socket) => {
// Delete groups that are not in the list
log.debug("socket", "Delete groups that are not in the list");
const slots = groupIDList.map(() => "?").join(",");
if (groupIDList.length === 0) {
await R.exec("DELETE FROM `group` WHERE status_page_id = ?", [ statusPage.id ]);
} else {
const slots = groupIDList.map(() => "?").join(",");
const data = [
...groupIDList,
statusPage.id
];
await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots}) AND status_page_id = ?`, data);
const data = [
...groupIDList,
statusPage.id
];
await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots}) AND status_page_id = ?`, data);
}
const server = UptimeKumaServer.getInstance();
@@ -288,6 +292,7 @@ module.exports.statusPageSocketHandler = (socket) => {
ok: true,
msg: "successAdded",
msgi18n: true,
slug: slug
});
} catch (error) {

View File

@@ -619,7 +619,7 @@ $shadow-box-padding: 20px;
bottom: 0;
margin-left: -$shadow-box-padding;
margin-right: -$shadow-box-padding;
z-index: 100;
z-index: 10;
background-color: rgba(white, 0.2);
backdrop-filter: blur(2px);
border-radius: 0 0 10px 10px;

View File

@@ -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">

View File

@@ -16,14 +16,11 @@
<label for="slug" class="form-label">{{ $t("Slug") }}</label>
<div class="input-group">
<span id="basic-addon3" class="input-group-text">/status/</span>
<input id="slug" v-model="slug" type="text" class="form-control" required data-testid="slug-input">
<input id="slug" v-model="slug" type="text" class="form-control" autocapitalize="none" required data-testid="slug-input">
</div>
<div class="form-text">
<ul>
<li>{{ $t("Accept characters:") }} <mark>a-z</mark> <mark>0-9</mark> <mark>-</mark></li>
<i18n-t tag="li" keypath="startOrEndWithOnly">
<mark>a-z</mark> <mark>0-9</mark>
</i18n-t>
<li>{{ $t("No consecutive dashes") }} <mark>--</mark></li>
<i18n-t tag="li" keypath="statusPageSpecialSlugDesc">
<mark class="me-1">default</mark>
@@ -65,7 +62,7 @@ export default {
this.processing = false;
if (res.ok) {
location.href = "/status/" + this.slug + "?edit";
location.href = "/status/" + res.slug + "?edit";
} else {
if (res.msg.includes("UNIQUE constraint")) {
@@ -85,4 +82,8 @@ export default {
.shadow-box {
padding: 20px;
}
#slug {
text-transform: lowercase;
}
</style>

View File

@@ -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;
@@ -1443,11 +1443,14 @@ message HealthCheckResponse {
}
}
if (this.monitor.type === "snmp") {
// Set a default timeout if the monitor type has changed or if it's a new monitor
if (oldType || this.isAdd) {
if (this.monitor.type === "snmp") {
// snmp is not expected to be executed via the internet => we can choose a lower default timeout
this.monitor.timeout = 5;
} else {
this.monitor.timeout = 48;
this.monitor.timeout = 5;
} else {
this.monitor.timeout = 48;
}
}
// Set default SNMP version