Merge branch 'master' into feature/umami-analytics-status-page
This commit is contained in:
commit
fd07cf7f7b
12
.github/workflows/auto-test.yml
vendored
12
.github/workflows/auto-test.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-22.04, windows-latest, ARM64]
|
||||
node: [ 18, 20 ]
|
||||
node: [ 20, 24 ]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm install
|
||||
@ -49,7 +49,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ARMv7 ]
|
||||
node: [ 18, 20 ]
|
||||
node: [ 20, 22 ]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
@ -57,7 +57,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm ci --production
|
||||
@ -70,7 +70,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
- run: npm install
|
||||
@ -84,7 +84,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
- run: npm install
|
||||
|
||||
4
.github/workflows/close-incorrect-issue.yml
vendored
4
.github/workflows/close-incorrect-issue.yml
vendored
@ -11,13 +11,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node-version: [18]
|
||||
node-version: [20]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
|
||||
2
.github/workflows/validate.yml
vendored
2
.github/workflows/validate.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
|
||||
@ -19,7 +19,4 @@ RUN apt update && \
|
||||
curl -sL https://deb.nodesource.com/setup_18.x | bash && \
|
||||
apt --yes --no-install-recommends install nodejs && \
|
||||
node ./extra/build-healthcheck.js $TARGETPLATFORM && \
|
||||
apt --yes remove nodejs && \
|
||||
apt autoremove -y --purge && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt --yes remove nodejs
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
# Download Apprise deb package
|
||||
FROM node:20-bookworm-slim AS download-apprise
|
||||
FROM node:22-bookworm-slim AS download-apprise
|
||||
WORKDIR /app
|
||||
COPY ./extra/download-apprise.mjs ./download-apprise.mjs
|
||||
RUN apt update && \
|
||||
apt --yes --no-install-recommends install curl && \
|
||||
npm install cheerio semver && \
|
||||
node ./download-apprise.mjs && \
|
||||
apt autoremove -y --purge && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
node ./download-apprise.mjs
|
||||
|
||||
# Base Image (Slim)
|
||||
# If the image changed, the second stage image should be changed too
|
||||
FROM node:20-bookworm-slim AS base2-slim
|
||||
FROM node:22-bookworm-slim AS base2-slim
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Specify --no-install-recommends to skip unused dependencies, make the base much smaller!
|
||||
@ -34,9 +31,8 @@ RUN apt update && \
|
||||
curl \
|
||||
sudo \
|
||||
nscd && \
|
||||
apt autoremove -y --purge && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
apt --yes autoremove
|
||||
|
||||
# apprise = for notifications (Install from the deb package, as the stable one is too old) (workaround for #4867)
|
||||
# Switching to testing repo is no longer working, as the testing repo is not bookworm anymore.
|
||||
@ -45,10 +41,9 @@ RUN apt update && \
|
||||
COPY --from=download-apprise /app/apprise.deb ./apprise.deb
|
||||
RUN apt update && \
|
||||
apt --yes --no-install-recommends install ./apprise.deb python3-paho-mqtt && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
rm -f apprise.deb && \
|
||||
apt autoremove -y --purge && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt --yes autoremove
|
||||
|
||||
# Install cloudflared
|
||||
RUN curl https://pkg.cloudflare.com/cloudflare-main.gpg --output /usr/share/keyrings/cloudflare-main.gpg && \
|
||||
@ -56,14 +51,14 @@ RUN curl https://pkg.cloudflare.com/cloudflare-main.gpg --output /usr/share/keyr
|
||||
apt update && \
|
||||
apt install --yes --no-install-recommends cloudflared && \
|
||||
cloudflared version && \
|
||||
apt autoremove -y --purge && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
apt --yes autoremove
|
||||
|
||||
# For nscd
|
||||
COPY ./docker/etc/nscd.conf /etc/nscd.conf
|
||||
COPY ./docker/etc/sudoers /etc/sudoers
|
||||
|
||||
|
||||
# Full Base Image
|
||||
# MariaDB, Chromium and fonts
|
||||
# Make sure to reuse the slim image here. Uncomment the above line if you want to build it from scratch.
|
||||
@ -72,7 +67,6 @@ FROM louislam/uptime-kuma:base2-slim AS base2
|
||||
ENV UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB=1
|
||||
RUN apt update && \
|
||||
apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk mariadb-server && \
|
||||
apt autoremove -y --purge && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
apt --yes autoremove && \
|
||||
chown -R node:node /var/lib/mysql
|
||||
|
||||
@ -70,10 +70,7 @@ RUN apt update \
|
||||
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
|
||||
&& apt update \
|
||||
&& apt --yes --no-install-recommends install git \
|
||||
&& apt autoremove -y --purge \
|
||||
&& apt clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
&& apt --yes --no-install-recommends install git
|
||||
|
||||
## Empty the directory, because we have to clone the Git repo.
|
||||
RUN rm -rf ./* && chown node /app
|
||||
@ -98,10 +95,7 @@ CMD ["npm", "run", "start-pr-test"]
|
||||
FROM louislam/uptime-kuma:base2 AS upload-artifact
|
||||
WORKDIR /
|
||||
RUN apt update && \
|
||||
apt --yes install curl file && \
|
||||
apt autoremove -y --purge && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt --yes install curl file
|
||||
|
||||
COPY --from=build /app /app
|
||||
|
||||
@ -121,3 +115,4 @@ RUN chmod +x /app/extra/upload-github-release-asset.sh
|
||||
# Dist only
|
||||
RUN cd /app && tar -zcvf $DIST dist
|
||||
RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=/app/$DIST
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"url": "https://github.com/louislam/uptime-kuma.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || >= 20.4.0"
|
||||
"node": ">= 20.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
|
||||
@ -27,7 +27,9 @@
|
||||
"build": "vite build --config ./config/vite.config.js",
|
||||
"test": "npm run test-backend && npm run test-e2e",
|
||||
"test-with-build": "npm run build && npm test",
|
||||
"test-backend": "cross-env TEST_BACKEND=1 node --test test/backend-test",
|
||||
"test-backend": "node test/test-backend.mjs",
|
||||
"test-backend-22": "cross-env TEST_BACKEND=1 node --test \"test/backend-test/**/*.js\"",
|
||||
"test-backend-20": "cross-env TEST_BACKEND=1 node --test test/backend-test",
|
||||
"test-e2e": "playwright test --config ./config/playwright.config.js",
|
||||
"test-e2e-ui": "playwright test --config ./config/playwright.config.js --ui --ui-port=51063",
|
||||
"playwright-codegen": "playwright codegen localhost:3000 --save-storage=./private/e2e-auth.json",
|
||||
|
||||
@ -578,7 +578,8 @@ class Monitor extends BeanModel {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID === this.id) {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) {
|
||||
log.info("monitor", res.data);
|
||||
}
|
||||
|
||||
|
||||
@ -2,13 +2,13 @@ const { MonitorType } = require("./monitor-type");
|
||||
const { chromium } = require("playwright-core");
|
||||
const { UP, log } = require("../../src/util");
|
||||
const { Settings } = require("../settings");
|
||||
const commandExistsSync = require("command-exists").sync;
|
||||
const childProcess = require("child_process");
|
||||
const path = require("path");
|
||||
const Database = require("../database");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const config = require("../config");
|
||||
const { RemoteBrowser } = require("../remote-browser");
|
||||
const { commandExists } = require("../util-server");
|
||||
|
||||
/**
|
||||
* Cached instance of a browser
|
||||
@ -122,7 +122,7 @@ async function prepareChromeExecutable(executablePath) {
|
||||
executablePath = "/usr/bin/chromium";
|
||||
|
||||
// Install chromium in container via apt install
|
||||
if ( !commandExistsSync(executablePath)) {
|
||||
if (! await commandExists(executablePath)) {
|
||||
await new Promise((resolve, reject) => {
|
||||
log.info("Chromium", "Installing Chromium...");
|
||||
let child = childProcess.exec("apt update && apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk");
|
||||
@ -146,7 +146,7 @@ async function prepareChromeExecutable(executablePath) {
|
||||
}
|
||||
|
||||
} else {
|
||||
executablePath = findChrome(allowedList);
|
||||
executablePath = await findChrome(allowedList);
|
||||
}
|
||||
} else {
|
||||
// User specified a path
|
||||
@ -160,20 +160,20 @@ async function prepareChromeExecutable(executablePath) {
|
||||
|
||||
/**
|
||||
* Find the chrome executable
|
||||
* @param {any[]} executables Executables to search through
|
||||
* @returns {any} Executable
|
||||
* @throws Could not find executable
|
||||
* @param {string[]} executables Executables to search through
|
||||
* @returns {Promise<string>} Executable
|
||||
* @throws {Error} Could not find executable
|
||||
*/
|
||||
function findChrome(executables) {
|
||||
async function findChrome(executables) {
|
||||
// Use the last working executable, so we don't have to search for it again
|
||||
if (lastAutoDetectChromeExecutable) {
|
||||
if (commandExistsSync(lastAutoDetectChromeExecutable)) {
|
||||
if (await commandExists(lastAutoDetectChromeExecutable)) {
|
||||
return lastAutoDetectChromeExecutable;
|
||||
}
|
||||
}
|
||||
|
||||
for (let executable of executables) {
|
||||
if (commandExistsSync(executable)) {
|
||||
if (await commandExists(executable)) {
|
||||
lastAutoDetectChromeExecutable = executable;
|
||||
return executable;
|
||||
}
|
||||
|
||||
@ -23,9 +23,7 @@ class PagerDuty extends NotificationProvider {
|
||||
|
||||
if (heartbeatJSON.status === UP) {
|
||||
const title = "Uptime Kuma Monitor ✅ Up";
|
||||
const eventAction = notification.pagerdutyAutoResolve || null;
|
||||
|
||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, eventAction);
|
||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "resolve");
|
||||
}
|
||||
|
||||
if (heartbeatJSON.status === DOWN) {
|
||||
@ -63,10 +61,6 @@ class PagerDuty extends NotificationProvider {
|
||||
*/
|
||||
async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") {
|
||||
|
||||
if (eventAction == null) {
|
||||
return "No action required";
|
||||
}
|
||||
|
||||
let monitorUrl;
|
||||
if (monitorInfo.type === "port") {
|
||||
monitorUrl = monitorInfo.hostname;
|
||||
@ -79,6 +73,13 @@ class PagerDuty extends NotificationProvider {
|
||||
monitorUrl = monitorInfo.url;
|
||||
}
|
||||
|
||||
if (eventAction === "resolve") {
|
||||
if (notification.pagerdutyAutoResolve === "0") {
|
||||
return "no action required";
|
||||
}
|
||||
eventAction = notification.pagerdutyAutoResolve;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: notification.pagerdutyIntegrationUrl,
|
||||
|
||||
@ -12,6 +12,8 @@ class Webhook extends NotificationProvider {
|
||||
const okMsg = "Sent Successfully.";
|
||||
|
||||
try {
|
||||
const httpMethod = notification.httpMethod.toLowerCase() || "post";
|
||||
|
||||
let data = {
|
||||
heartbeat: heartbeatJSON,
|
||||
monitor: monitorJSON,
|
||||
@ -21,7 +23,19 @@ class Webhook extends NotificationProvider {
|
||||
headers: {}
|
||||
};
|
||||
|
||||
if (notification.webhookContentType === "form-data") {
|
||||
if (httpMethod === "get") {
|
||||
config.params = {
|
||||
msg: msg
|
||||
};
|
||||
|
||||
if (heartbeatJSON) {
|
||||
config.params.heartbeat = JSON.stringify(heartbeatJSON);
|
||||
}
|
||||
|
||||
if (monitorJSON) {
|
||||
config.params.monitor = JSON.stringify(monitorJSON);
|
||||
}
|
||||
} else if (notification.webhookContentType === "form-data") {
|
||||
const formData = new FormData();
|
||||
formData.append("data", JSON.stringify(data));
|
||||
config.headers = formData.getHeaders();
|
||||
@ -42,7 +56,13 @@ class Webhook extends NotificationProvider {
|
||||
}
|
||||
|
||||
config = this.getAxiosConfigWithProxy(config);
|
||||
await axios.post(notification.webhookURL, data, config);
|
||||
|
||||
if (httpMethod === "get") {
|
||||
await axios.get(notification.webhookURL, config);
|
||||
} else {
|
||||
await axios.post(notification.webhookURL, data, config);
|
||||
}
|
||||
|
||||
return okMsg;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
@ -81,6 +81,7 @@ const Brevo = require("./notification-providers/brevo");
|
||||
const YZJ = require("./notification-providers/yzj");
|
||||
const SMSPlanet = require("./notification-providers/sms-planet");
|
||||
const SpugPush = require("./notification-providers/spugpush");
|
||||
const { commandExists } = require("./util-server");
|
||||
|
||||
class Notification {
|
||||
providerList = {};
|
||||
@ -275,12 +276,10 @@ class Notification {
|
||||
|
||||
/**
|
||||
* Check if apprise exists
|
||||
* @returns {boolean} Does the command apprise exist?
|
||||
* @returns {Promise<boolean>} Does the command apprise exist?
|
||||
*/
|
||||
static checkApprise() {
|
||||
let commandExistsSync = require("command-exists").sync;
|
||||
let exists = commandExistsSync("apprise");
|
||||
return exists;
|
||||
static async checkApprise() {
|
||||
return await commandExists("apprise");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1503,7 +1503,7 @@ let needSetup = false;
|
||||
socket.on("checkApprise", async (callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
callback(Notification.checkApprise());
|
||||
callback(await Notification.checkApprise());
|
||||
} catch (e) {
|
||||
callback(false);
|
||||
}
|
||||
|
||||
@ -1116,3 +1116,19 @@ function fsExists(path) {
|
||||
});
|
||||
}
|
||||
module.exports.fsExists = fsExists;
|
||||
|
||||
/**
|
||||
* By default, command-exists will throw a null error if the command does not exist, which is ugly. The function makes it better.
|
||||
* Read more: https://github.com/mathisonian/command-exists/issues/22
|
||||
* @param {string} command Command to check
|
||||
* @returns {Promise<boolean>} True if command exists, false otherwise
|
||||
*/
|
||||
async function commandExists(command) {
|
||||
try {
|
||||
await require("command-exists")(command);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
module.exports.commandExists = commandExists;
|
||||
|
||||
@ -12,6 +12,21 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="webhook-http-method" class="form-label">{{ $t("HTTP Method") }}</label>
|
||||
<select
|
||||
id="webhook-http-method"
|
||||
v-model="$parent.notification.httpMethod"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="post">POST</option>
|
||||
<option value="get">GET</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
{{ $parent.notification.httpMethod === 'get' ? $t("webhookGetMethodDesc") : $t("webhookPostMethodDesc") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="$parent.notification.httpMethod === 'post'" class="mb-3">
|
||||
<label for="webhook-request-body" class="form-label">{{ $t("Request Body") }}</label>
|
||||
<select
|
||||
id="webhook-request-body"
|
||||
@ -82,6 +97,11 @@ export default {
|
||||
]);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (typeof this.$parent.notification.httpMethod === "undefined") {
|
||||
this.$parent.notification.httpMethod = "post";
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@ -306,6 +306,7 @@
|
||||
"Show Tags": "Show Tags",
|
||||
"Hide Tags": "Hide Tags",
|
||||
"Description": "Description",
|
||||
"descriptionHelpText": "Shown on the internal dashboard. Markdown is allowed and sanitized (preserves spaces and indentation) before display.",
|
||||
"No monitors available.": "No monitors available.",
|
||||
"Add one": "Add one",
|
||||
"No Monitors": "No Monitors",
|
||||
@ -928,6 +929,9 @@
|
||||
"noGroupMonitorMsg": "Not Available. Create a Group Monitor First.",
|
||||
"Close": "Close",
|
||||
"Request Body": "Request Body",
|
||||
"HTTP Method": "HTTP Method",
|
||||
"webhookPostMethodDesc": "POST is good for most modern HTTP servers.",
|
||||
"webhookGetMethodDesc": "GET sends data as query parameters and does not allow configuring a body. Useful for triggering Uptime Kuma Push monitors.",
|
||||
"wayToGetFlashDutyKey": "To integrate Uptime Kuma with Flashduty: Go to Channels > Select a channel > Integrations > Add a new integration, choose Uptime Kuma, and copy the Push URL.",
|
||||
"FlashDuty Severity": "Severity",
|
||||
"FlashDuty Push URL": "Push URL",
|
||||
|
||||
@ -819,6 +819,7 @@
|
||||
<div class="my-3">
|
||||
<label for="description" class="form-label">{{ $t("Description") }}</label>
|
||||
<input id="description" v-model="monitor.description" type="text" class="form-control">
|
||||
<div class="form-text">{{ $t("descriptionHelpText") }}</div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
|
||||
12
test/test-backend.mjs
Normal file
12
test/test-backend.mjs
Normal file
@ -0,0 +1,12 @@
|
||||
import * as childProcess from "child_process";
|
||||
|
||||
const version = parseInt(process.version.slice(1).split(".")[0]);
|
||||
|
||||
/**
|
||||
* Since Node.js 22 introduced a different "node --test" command with glob, we need to run different test commands based on the Node.js version.
|
||||
*/
|
||||
if (version < 22) {
|
||||
childProcess.execSync("npm run test-backend-20", { stdio: "inherit" });
|
||||
} else {
|
||||
childProcess.execSync("npm run test-backend-22", { stdio: "inherit" });
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user