mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-07-18 07:24:02 +02:00
Merge branch 'master' into fix-sync
This commit is contained in:
commit
6278267681
25 changed files with 175 additions and 105 deletions
2
.github/ISSUE_TEMPLATE/ask_for_help.yml
vendored
2
.github/ISSUE_TEMPLATE/ask_for_help.yml
vendored
|
@ -3,7 +3,7 @@ name: ❓ Ask for help
|
|||
description: |
|
||||
Submit any question related to Uptime Kuma
|
||||
#title: "[Help]"
|
||||
labels: ["help", "P3-low"]
|
||||
labels: ["help"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
|
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -3,7 +3,7 @@ name: 🐛 Bug Report
|
|||
description: |
|
||||
Submit a bug report to help us improve
|
||||
#title: "[Bug]"
|
||||
labels: ["bug", "P2-medium"]
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
|
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -3,7 +3,7 @@ name: 🚀 Feature Request
|
|||
description: |
|
||||
Submit a proposal for a new feature
|
||||
# title: "[Feature]"
|
||||
labels: ["feature-request", "P3-low"]
|
||||
labels: ["feature-request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
|
2
.github/ISSUE_TEMPLATE/security_issue.yml
vendored
2
.github/ISSUE_TEMPLATE/security_issue.yml
vendored
|
@ -3,7 +3,7 @@ name: 🛡️ Security Issue
|
|||
description: |
|
||||
Notify Louis Lam about a security concern. Please do NOT include any sensitive details in this issue.
|
||||
# title: "Security Issue"
|
||||
labels: ["security", "P1-high"]
|
||||
labels: ["security"]
|
||||
assignees: [louislam]
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
55
.github/PULL_REQUEST_TEMPLATE.md
vendored
55
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,10 +1,10 @@
|
|||
**⚠️ Please Note: We do not accept all types of pull requests, and we want to ensure we don’t waste your time. Before submitting, make sure you have read our pull request guidelines: [Pull Request Rules](https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md#can-i-create-a-pull-request-for-uptime-kuma)**
|
||||
|
||||
## ❗ Important Announcement
|
||||
|
||||
<details><summary>Click here for more details:</summary>
|
||||
</p>
|
||||
|
||||
**⚠️ Please Note: We do not accept all types of pull requests, and we want to ensure we don’t waste your time. Before submitting, make sure you have read our pull request guidelines: [Pull Request Rules](https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md#can-i-create-a-pull-request-for-uptime-kuma)**
|
||||
|
||||
### 🚧 Temporary Delay in Feature Requests and Pull Request Reviews
|
||||
|
||||
**At this time, we may be slower to respond to new feature requests and review pull requests. Existing requests and PRs will remain in the backlog but may not be prioritized immediately.**
|
||||
|
@ -26,16 +26,22 @@ We appreciate your patience and understanding as we continue to improve Uptime K
|
|||
|
||||
## 📋 Overview
|
||||
|
||||
Provide a clear summary of the purpose and scope of this pull request:
|
||||
<!-- Provide a clear summary of the purpose and scope of this pull request:-->
|
||||
|
||||
- **What problem does this pull request address?**
|
||||
|
||||
- Please provide a detailed explanation here.
|
||||
|
||||
- **What features or functionality does this pull request introduce or enhance?**
|
||||
|
||||
- Please provide a detailed explanation here.
|
||||
|
||||
## 🔗 Related Issues
|
||||
|
||||
<!--
|
||||
Please link any GitHub issues or tasks that this pull request addresses. Use the appropriate issue numbers or links.
|
||||
-->
|
||||
|
||||
- Relates to #issue-number
|
||||
- Resolves #issue-number
|
||||
|
||||
## 🔄 Changes
|
||||
|
||||
### 🛠️ Type of change
|
||||
|
@ -52,19 +58,7 @@ Provide a clear summary of the purpose and scope of this pull request:
|
|||
- [ ] 🔧 Other (please specify):
|
||||
- Provide additional details here.
|
||||
|
||||
## 🔗 Related Issues
|
||||
|
||||
<!--
|
||||
Please link any GitHub issues or tasks that this pull request addresses. Use the appropriate issue numbers or links.
|
||||
|
||||
**Note**: Include only issues directly related to this PR. Remove any irrelevant reference.
|
||||
-->
|
||||
|
||||
- Relates to #issue-number
|
||||
- Resolves #issue-number
|
||||
- Fixes #issue-number
|
||||
|
||||
## 📄 Checklist *
|
||||
## 📄 Checklist
|
||||
|
||||
<!-- Please select all options that apply -->
|
||||
|
||||
|
@ -97,26 +91,3 @@ If not, remove this section.
|
|||
| `DOWN` |  |  |
|
||||
| Certificate-expiry |  |  |
|
||||
| Testing |  |  |
|
||||
|
||||
## ℹ️ Additional Context
|
||||
|
||||
Provide any relevant details to assist reviewers in understanding the changes.
|
||||
|
||||
<details><summary>Click here for more details:</summary>
|
||||
</p>
|
||||
|
||||
**Key Considerations**:
|
||||
|
||||
- **Design decisions** – Key choices or trade-offs made during development.
|
||||
- **Alternative solutions** – Approaches considered but not implemented, along with reasons.
|
||||
- **Relevant links** – Specifications, discussions, or resources that provide context.
|
||||
- **Dependencies** – Related pull requests or issues that must be resolved before merging.
|
||||
- **Additional context** – Any other details that may help reviewers understand the changes.
|
||||
|
||||
Provide details here
|
||||
|
||||
## 💬 Requested Feedback
|
||||
|
||||
<!-- If a part of our docs is unclear, you are unsure how to do something/.. this is where we would appreciate your feedback -->
|
||||
|
||||
- `Mention documents needing feedback here`
|
||||
|
|
|
@ -79,6 +79,10 @@ USER node
|
|||
RUN git config --global user.email "no-reply@no-reply.com"
|
||||
RUN git config --global user.name "PR Tester"
|
||||
RUN git clone https://github.com/louislam/uptime-kuma.git .
|
||||
|
||||
# Hide the warning when running in detached head state
|
||||
RUN git config --global advice.detachedHead false
|
||||
|
||||
RUN npm ci
|
||||
|
||||
EXPOSE 3000 3001
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
const childProcess = require("child_process");
|
||||
|
||||
if (!process.env.UPTIME_KUMA_GH_REPO) {
|
||||
console.error("Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let inputArray = process.env.UPTIME_KUMA_GH_REPO.split(":");
|
||||
|
||||
if (inputArray.length !== 2) {
|
||||
console.error("Invalid format. Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)");
|
||||
}
|
||||
|
||||
let name = inputArray[0];
|
||||
let branch = inputArray[1];
|
||||
|
||||
console.log("Checkout pr");
|
||||
|
||||
// Checkout the pr
|
||||
let result = childProcess.spawnSync("git", [ "remote", "add", name, `https://github.com/${name}/uptime-kuma` ]);
|
||||
|
||||
console.log(result.stdout.toString());
|
||||
console.error(result.stderr.toString());
|
||||
|
||||
result = childProcess.spawnSync("git", [ "fetch", name, branch ]);
|
||||
|
||||
console.log(result.stdout.toString());
|
||||
console.error(result.stderr.toString());
|
||||
|
||||
result = childProcess.spawnSync("git", [ "checkout", `${name}/${branch}`, "--force" ]);
|
||||
|
||||
console.log(result.stdout.toString());
|
||||
console.error(result.stderr.toString());
|
34
extra/checkout-pr.mjs
Normal file
34
extra/checkout-pr.mjs
Normal file
|
@ -0,0 +1,34 @@
|
|||
import childProcess from "child_process";
|
||||
import { parsePrName } from "./kuma-pr/pr-lib.mjs";
|
||||
|
||||
let { name, branch } = parsePrName(process.env.UPTIME_KUMA_GH_REPO);
|
||||
|
||||
console.log(`Checking out PR from ${name}:${branch}`);
|
||||
|
||||
// Checkout the pr
|
||||
let result = childProcess.spawnSync("git", [ "remote", "add", name, `https://github.com/${name}/uptime-kuma` ], {
|
||||
stdio: "inherit"
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error("Failed to add remote repository.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
result = childProcess.spawnSync("git", [ "fetch", name, branch ], {
|
||||
stdio: "inherit"
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error("Failed to fetch the branch.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
result = childProcess.spawnSync("git", [ "checkout", `${name}/${branch}`, "--force" ], {
|
||||
stdio: "inherit"
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error("Failed to checkout the branch.");
|
||||
process.exit(1);
|
||||
}
|
26
extra/kuma-pr/index.mjs
Normal file
26
extra/kuma-pr/index.mjs
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env node
|
||||
import { spawn } from "child_process";
|
||||
import { parsePrName } from "./pr-lib.mjs";
|
||||
|
||||
const prName = process.argv[2];
|
||||
|
||||
// Pre-check the prName here, so testers don't need to wait until the Docker image is pulled to see the error.
|
||||
try {
|
||||
parsePrName(prName);
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
spawn("docker", [
|
||||
"run",
|
||||
"--rm",
|
||||
"-it",
|
||||
"-p", "3000:3000",
|
||||
"-p", "3001:3001",
|
||||
"--pull", "always",
|
||||
"-e", `UPTIME_KUMA_GH_REPO=${prName}`,
|
||||
"louislam/uptime-kuma:pr-test2"
|
||||
], {
|
||||
stdio: "inherit",
|
||||
});
|
8
extra/kuma-pr/package.json
Normal file
8
extra/kuma-pr/package.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "kuma-pr",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"kuma-pr": "./index.mjs"
|
||||
}
|
||||
}
|
39
extra/kuma-pr/pr-lib.mjs
Normal file
39
extra/kuma-pr/pr-lib.mjs
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Parse <name>:<branch> to an object.
|
||||
* @param {string} prName <name>:<branch>
|
||||
* @returns {object} An object with name and branch properties.
|
||||
*/
|
||||
export function parsePrName(prName) {
|
||||
let name = "louislam";
|
||||
let branch;
|
||||
|
||||
const errorMessage = "Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)";
|
||||
|
||||
if (!prName) {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
prName = prName.trim();
|
||||
if (prName === "") {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
let inputArray = prName.split(":");
|
||||
|
||||
// Just realized that owner's prs are not prefixed with "louislam:"
|
||||
if (inputArray.length === 1) {
|
||||
branch = inputArray[0];
|
||||
|
||||
} else if (inputArray.length === 2) {
|
||||
name = inputArray[0];
|
||||
branch = inputArray[1];
|
||||
|
||||
} else {
|
||||
throw new Error("Invalid format. The format is like this: mhkarimi1383:goalert-notification");
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
branch
|
||||
};
|
||||
}
|
|
@ -57,7 +57,7 @@
|
|||
"release-nightly": "node ./extra/release/nightly.mjs",
|
||||
"git-remove-tag": "git tag -d",
|
||||
"build-dist-and-restart": "npm run build && npm run start-server-dev",
|
||||
"start-pr-test": "node extra/checkout-pr.js && npm install && npm run dev",
|
||||
"start-pr-test": "node extra/checkout-pr.mjs && npm install && npm run dev",
|
||||
"build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go",
|
||||
"deploy-demo-server": "node extra/deploy-demo-server.js",
|
||||
"sort-contributors": "node extra/sort-contributors.js",
|
||||
|
|
|
@ -26,7 +26,7 @@ exports.login = async function (username, password) {
|
|||
// Upgrade the hash to bcrypt
|
||||
if (passwordHash.needRehash(user.password)) {
|
||||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
|
||||
passwordHash.generate(password),
|
||||
await passwordHash.generate(password),
|
||||
user.id,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const fs = require("fs");
|
||||
const fsAsync = fs.promises;
|
||||
const { R } = require("redbean-node");
|
||||
const { setSetting, setting } = require("./util-server");
|
||||
const { log, sleep } = require("../src/util");
|
||||
|
@ -707,12 +708,12 @@ class Database {
|
|||
|
||||
/**
|
||||
* Get the size of the database (SQLite only)
|
||||
* @returns {number} Size of database
|
||||
* @returns {Promise<number>} Size of database
|
||||
*/
|
||||
static getSize() {
|
||||
static async getSize() {
|
||||
if (Database.dbConfig.type === "sqlite") {
|
||||
log.debug("db", "Database.getSize()");
|
||||
let stats = fs.statSync(Database.sqlitePath);
|
||||
let stats = await fsAsync.stat(Database.sqlitePath);
|
||||
log.debug("db", stats);
|
||||
return stats.size;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class User extends BeanModel {
|
|||
*/
|
||||
static async resetPassword(userID, newPassword) {
|
||||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
|
||||
passwordHash.generate(newPassword),
|
||||
await passwordHash.generate(newPassword),
|
||||
userID
|
||||
]);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class User extends BeanModel {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
async resetPassword(newPassword) {
|
||||
const hashedPassword = passwordHash.generate(newPassword);
|
||||
const hashedPassword = await passwordHash.generate(newPassword);
|
||||
|
||||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
|
||||
hashedPassword,
|
||||
|
|
|
@ -5,10 +5,10 @@ const saltRounds = 10;
|
|||
/**
|
||||
* Hash a password
|
||||
* @param {string} password Password to hash
|
||||
* @returns {string} Hash
|
||||
* @returns {Promise<string>} Hash
|
||||
*/
|
||||
exports.generate = function (password) {
|
||||
return bcrypt.hashSync(password, saltRounds);
|
||||
return bcrypt.hash(password, saltRounds);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -674,7 +674,7 @@ let needSetup = false;
|
|||
|
||||
let user = R.dispense("user");
|
||||
user.username = username;
|
||||
user.password = passwordHash.generate(password);
|
||||
user.password = await passwordHash.generate(password);
|
||||
await R.store(user);
|
||||
|
||||
needSetup = false;
|
||||
|
|
|
@ -20,7 +20,7 @@ module.exports.apiKeySocketHandler = (socket) => {
|
|||
checkLogin(socket);
|
||||
|
||||
let clearKey = nanoid(40);
|
||||
let hashedKey = passwordHash.generate(clearKey);
|
||||
let hashedKey = await passwordHash.generate(clearKey);
|
||||
key["key"] = hashedKey;
|
||||
let bean = await APIKey.save(key, socket.userID);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ module.exports.databaseSocketHandler = (socket) => {
|
|||
checkLogin(socket);
|
||||
callback({
|
||||
ok: true,
|
||||
size: Database.getSize(),
|
||||
size: await Database.getSize(),
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
|
|
|
@ -4,7 +4,7 @@ const { sendInfo } = require("../client");
|
|||
const { checkLogin } = require("../util-server");
|
||||
const GameResolver = require("gamedig/lib/GameResolver");
|
||||
const { testChrome } = require("../monitor-types/real-browser-monitor-type");
|
||||
const fs = require("fs");
|
||||
const fsAsync = require("fs").promises;
|
||||
const path = require("path");
|
||||
|
||||
let gameResolver = new GameResolver();
|
||||
|
@ -90,7 +90,7 @@ module.exports.generalSocketHandler = (socket, server) => {
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("getPushExample", (language, callback) => {
|
||||
socket.on("getPushExample", async (language, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
if (!/^[a-z-]+$/.test(language)) {
|
||||
|
@ -106,13 +106,13 @@ module.exports.generalSocketHandler = (socket, server) => {
|
|||
|
||||
try {
|
||||
let dir = path.join("./extra/push-examples", language);
|
||||
let files = fs.readdirSync(dir);
|
||||
let files = await fsAsync.readdir(dir);
|
||||
|
||||
for (let file of files) {
|
||||
if (file.startsWith("index.")) {
|
||||
callback({
|
||||
ok: true,
|
||||
code: fs.readFileSync(path.join(dir, file), "utf8"),
|
||||
code: await fsAsync.readFile(path.join(dir, file), "utf8"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ exports.initJWTSecret = async () => {
|
|||
jwtSecretBean.key = "jwtSecret";
|
||||
}
|
||||
|
||||
jwtSecretBean.value = passwordHash.generate(genSecret());
|
||||
jwtSecretBean.value = await passwordHash.generate(genSecret());
|
||||
await R.store(jwtSecretBean);
|
||||
return jwtSecretBean;
|
||||
};
|
||||
|
|
19
src/i18n.js
19
src/i18n.js
|
@ -75,11 +75,20 @@ export function currentLocale() {
|
|||
if (locale in messages) {
|
||||
return locale;
|
||||
}
|
||||
// some locales are further specified such as "en-US".
|
||||
// If we only have a generic locale for this, we can use it too
|
||||
const genericLocale = locale.split("-")[0];
|
||||
if (genericLocale in messages) {
|
||||
return genericLocale;
|
||||
// If the locale is a 2-letter code, we can try to find a regional variant
|
||||
// e.g. "fr" may not be in the messages, but "fr-FR" is
|
||||
if (locale.length === 2) {
|
||||
const regionalLocale = `${locale}-${locale.toUpperCase()}`;
|
||||
if (regionalLocale in messages) {
|
||||
return regionalLocale;
|
||||
}
|
||||
} else {
|
||||
// Some locales are further specified such as "en-US".
|
||||
// If we only have a generic locale for this, we can use it too
|
||||
const genericLocale = locale.slice(0, 2);
|
||||
if (genericLocale in messages) {
|
||||
return genericLocale;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "en";
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<span class="fs-4 title">{{ $t("Uptime Kuma") }}</span>
|
||||
</router-link>
|
||||
|
||||
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/uptime-kuma/releases" class="btn btn-info me-3">
|
||||
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/uptime-kuma/releases" class="btn btn-primary me-3">
|
||||
<font-awesome-icon icon="arrow-alt-circle-up" /> {{ $t("New Update") }}
|
||||
</a>
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<div>{{ monitor.id }}</div>
|
||||
</div>
|
||||
</h1>
|
||||
<p v-if="monitor.description">{{ monitor.description }}</p>
|
||||
<!-- eslint-disable-next-line vue/no-v-html-->
|
||||
<p v-if="monitor.description" v-html="descriptionHTML"></p>
|
||||
<div class="d-flex">
|
||||
<div class="tags">
|
||||
<Tag v-for="tag in monitor.tags" :key="tag.id" :item="tag" :size="'sm'" />
|
||||
|
@ -285,6 +286,8 @@ import Tag from "../components/Tag.vue";
|
|||
import CertificateInfo from "../components/CertificateInfo.vue";
|
||||
import { getMonitorRelativeURL } from "../util.ts";
|
||||
import { URL } from "whatwg-url";
|
||||
import DOMPurify from "dompurify";
|
||||
import { marked } from "marked";
|
||||
import { getResBaseURL } from "../util-frontend";
|
||||
import { highlight, languages } from "prismjs/components/prism-core";
|
||||
import "prismjs/components/prism-clike";
|
||||
|
@ -399,6 +402,14 @@ export default {
|
|||
|
||||
screenshotURL() {
|
||||
return getResBaseURL() + this.monitor.screenshot + "?time=" + this.cacheTime;
|
||||
},
|
||||
|
||||
descriptionHTML() {
|
||||
if (this.monitor.description != null) {
|
||||
return DOMPurify.sanitize(marked(this.monitor.description));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -157,12 +157,12 @@
|
|||
<!-- Admin functions -->
|
||||
<div v-if="hasToken" class="mb-4">
|
||||
<div v-if="!enableEditMode">
|
||||
<button class="btn btn-info me-2" data-testid="edit-button" @click="edit">
|
||||
<button class="btn btn-primary me-2" data-testid="edit-button" @click="edit">
|
||||
<font-awesome-icon icon="edit" />
|
||||
{{ $t("Edit Status Page") }}
|
||||
</button>
|
||||
|
||||
<a href="/manage-status-page" class="btn btn-info">
|
||||
<a href="/manage-status-page" class="btn btn-primary">
|
||||
<font-awesome-icon icon="tachometer-alt" />
|
||||
{{ $t("Go to Dashboard") }}
|
||||
</a>
|
||||
|
|
Loading…
Add table
Reference in a new issue