mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-12-30 01:36:28 -05:00
Merge with master
Signed-off-by: Ivan Bratović <ivanbratovic4@gmail.com>
This commit is contained in:
commit
734762b773
2
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
2
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
@ -1,6 +1,6 @@
|
|||||||
name: "❓ Ask for help"
|
name: "❓ Ask for help"
|
||||||
description: "Submit any question related to Uptime Kuma"
|
description: "Submit any question related to Uptime Kuma"
|
||||||
title: "[Help] "
|
#title: "[Help] "
|
||||||
labels: [help]
|
labels: [help]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
|
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -1,6 +1,6 @@
|
|||||||
name: "🐛 Bug Report"
|
name: "🐛 Bug Report"
|
||||||
description: "Submit a bug report to help us improve"
|
description: "Submit a bug report to help us improve"
|
||||||
title: "[Bug] "
|
#title: "[Bug] "
|
||||||
labels: [bug]
|
labels: [bug]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@ -1,6 +1,6 @@
|
|||||||
name: 🚀 Feature Request
|
name: 🚀 Feature Request
|
||||||
description: "Submit a proposal for a new feature"
|
description: "Submit a proposal for a new feature"
|
||||||
title: "[Feature] "
|
#title: "[Feature] "
|
||||||
labels: [enhancement]
|
labels: [enhancement]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -24,3 +24,5 @@ Please delete options that are not relevant.
|
|||||||
- [ ] My code needed automated testing. I have added them (this is optional task)
|
- [ ] My code needed automated testing. I have added them (this is optional task)
|
||||||
|
|
||||||
## Screenshots (if any)
|
## Screenshots (if any)
|
||||||
|
|
||||||
|
Please do not use any external image service. Instead, just paste in or drag and drop the image here and it will be uploaded automatically.
|
||||||
|
@ -35,7 +35,7 @@ let options = {
|
|||||||
|
|
||||||
let request = client.request(options, (res) => {
|
let request = client.request(options, (res) => {
|
||||||
console.log(`Health Check OK [Res Code: ${res.statusCode}]`);
|
console.log(`Health Check OK [Res Code: ${res.statusCode}]`);
|
||||||
if (res.statusCode === 200) {
|
if (res.statusCode === 302) {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
} else {
|
} else {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
"args-parser": "~1.3.0",
|
"args-parser": "~1.3.0",
|
||||||
"axios": "~0.21.4",
|
"axios": "~0.21.4",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"bootstrap": "~5.1.3",
|
"bootstrap": "5.1.3",
|
||||||
"bree": "~6.3.1",
|
"bree": "~6.3.1",
|
||||||
"chardet": "^1.3.0",
|
"chardet": "^1.3.0",
|
||||||
"chart.js": "~3.6.0",
|
"chart.js": "~3.6.0",
|
||||||
|
@ -141,6 +141,7 @@ class Monitor extends BeanModel {
|
|||||||
// Do not do any queries/high loading things before the "bean.ping"
|
// Do not do any queries/high loading things before the "bean.ping"
|
||||||
let startTime = dayjs().valueOf();
|
let startTime = dayjs().valueOf();
|
||||||
|
|
||||||
|
debug(`[${this.name}] Prepare Options for axios`);
|
||||||
const options = {
|
const options = {
|
||||||
url: this.url,
|
url: this.url,
|
||||||
method: (this.method || "get").toLowerCase(),
|
method: (this.method || "get").toLowerCase(),
|
||||||
@ -160,6 +161,8 @@ class Monitor extends BeanModel {
|
|||||||
return checkStatusCode(status, this.getAcceptedStatuscodes());
|
return checkStatusCode(status, this.getAcceptedStatuscodes());
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug(`[${this.name}] Axios Request`);
|
||||||
let res = await axios.request(options);
|
let res = await axios.request(options);
|
||||||
bean.msg = `${res.status} - ${res.statusText}`;
|
bean.msg = `${res.status} - ${res.statusText}`;
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
bean.ping = dayjs().valueOf() - startTime;
|
||||||
@ -167,12 +170,13 @@ class Monitor extends BeanModel {
|
|||||||
// Check certificate if https is used
|
// Check certificate if https is used
|
||||||
let certInfoStartTime = dayjs().valueOf();
|
let certInfoStartTime = dayjs().valueOf();
|
||||||
if (this.getUrl()?.protocol === "https:") {
|
if (this.getUrl()?.protocol === "https:") {
|
||||||
|
debug(`[${this.name}] Check cert`);
|
||||||
try {
|
try {
|
||||||
let tlsInfoObject = checkCertificate(res);
|
let tlsInfoObject = checkCertificate(res);
|
||||||
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
|
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
|
||||||
|
|
||||||
if (!this.getIgnoreTls()) {
|
if (!this.getIgnoreTls()) {
|
||||||
debug("call sendCertNotification");
|
debug(`[${this.name}] call sendCertNotification`);
|
||||||
await this.sendCertNotification(tlsInfoObject);
|
await this.sendCertNotification(tlsInfoObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,15 +355,19 @@ class Monitor extends BeanModel {
|
|||||||
|
|
||||||
let beatInterval = this.interval;
|
let beatInterval = this.interval;
|
||||||
|
|
||||||
|
debug(`[${this.name}] Check isImportant`);
|
||||||
let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status);
|
let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status);
|
||||||
|
|
||||||
// Mark as important if status changed, ignore pending pings,
|
// Mark as important if status changed, ignore pending pings,
|
||||||
// Don't notify if disrupted changes to up
|
// Don't notify if disrupted changes to up
|
||||||
if (isImportant) {
|
if (isImportant) {
|
||||||
bean.important = true;
|
bean.important = true;
|
||||||
|
|
||||||
|
debug(`[${this.name}] sendNotification`);
|
||||||
await Monitor.sendNotification(isFirstBeat, this, bean);
|
await Monitor.sendNotification(isFirstBeat, this, bean);
|
||||||
|
|
||||||
// Clear Status Page Cache
|
// Clear Status Page Cache
|
||||||
|
debug(`[${this.name}] apicache clear`);
|
||||||
apicache.clear();
|
apicache.clear();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -377,10 +385,14 @@ class Monitor extends BeanModel {
|
|||||||
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug(`[${this.name}] Send to socket`);
|
||||||
io.to(this.user_id).emit("heartbeat", bean.toJSON());
|
io.to(this.user_id).emit("heartbeat", bean.toJSON());
|
||||||
Monitor.sendStats(io, this.id, this.user_id);
|
Monitor.sendStats(io, this.id, this.user_id);
|
||||||
|
|
||||||
|
debug(`[${this.name}] Store`);
|
||||||
await R.store(bean);
|
await R.store(bean);
|
||||||
|
|
||||||
|
debug(`[${this.name}] prometheus.update`);
|
||||||
prometheus.update(bean, tlsInfo);
|
prometheus.update(bean, tlsInfo);
|
||||||
|
|
||||||
previousBeat = bean;
|
previousBeat = bean;
|
||||||
@ -394,7 +406,10 @@ class Monitor extends BeanModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug(`[${this.name}] SetTimeout for next check.`);
|
||||||
this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000);
|
this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000);
|
||||||
|
} else {
|
||||||
|
console.log(`[${this.name}] isStop = true, no next check.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -27,7 +27,7 @@ class Feishu extends NotificationProvider {
|
|||||||
content: {
|
content: {
|
||||||
post: {
|
post: {
|
||||||
zh_cn: {
|
zh_cn: {
|
||||||
title: "UptimeKuma Alert: " + monitorJSON["name"],
|
title: "UptimeKuma Alert: [Down] " + monitorJSON["name"],
|
||||||
content: [
|
content: [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -54,7 +54,7 @@ class Feishu extends NotificationProvider {
|
|||||||
content: {
|
content: {
|
||||||
post: {
|
post: {
|
||||||
zh_cn: {
|
zh_cn: {
|
||||||
title: "UptimeKuma Alert: " + monitorJSON["name"],
|
title: "UptimeKuma Alert: [Up] " + monitorJSON["name"],
|
||||||
content: [
|
content: [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -101,6 +101,10 @@ router.get("/api/status-page/config", async (_request, response) => {
|
|||||||
config.statusPagePublished = true;
|
config.statusPagePublished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! config.statusPageTags) {
|
||||||
|
config.statusPageTags = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (! config.title) {
|
if (! config.title) {
|
||||||
config.title = "Uptime Kuma";
|
config.title = "Uptime Kuma";
|
||||||
}
|
}
|
||||||
@ -140,10 +144,25 @@ router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request,
|
|||||||
try {
|
try {
|
||||||
await checkPublished();
|
await checkPublished();
|
||||||
const publicGroupList = [];
|
const publicGroupList = [];
|
||||||
let list = await R.find("group", " public = 1 ORDER BY weight ");
|
const tagsVisible = (await getSettings("statusPage")).statusPageTags;
|
||||||
|
const list = await R.find("group", " public = 1 ORDER BY weight ");
|
||||||
for (let groupBean of list) {
|
for (let groupBean of list) {
|
||||||
publicGroupList.push(await groupBean.toPublicJSON());
|
let monitorGroup = await groupBean.toPublicJSON();
|
||||||
|
if (tagsVisible) {
|
||||||
|
monitorGroup.monitorList = await Promise.all(monitorGroup.monitorList.map(async (monitor) => {
|
||||||
|
// Includes tags as an array in response, allows for tags to be displayed on public status page
|
||||||
|
const tags = await R.getAll(
|
||||||
|
`SELECT monitor_tag.monitor_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 = ?`, [monitor.id]
|
||||||
|
);
|
||||||
|
return {...monitor, tags: tags}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
publicGroupList.push(monitorGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.json(publicGroupList);
|
response.json(publicGroupList);
|
||||||
|
@ -186,6 +186,15 @@ exports.entryPage = "dashboard";
|
|||||||
// Normal Router here
|
// Normal Router here
|
||||||
// ***************************
|
// ***************************
|
||||||
|
|
||||||
|
// Entry Page
|
||||||
|
app.get("/", async (_request, response) => {
|
||||||
|
if (exports.entryPage === "statusPage") {
|
||||||
|
response.redirect("/status");
|
||||||
|
} else {
|
||||||
|
response.redirect("/dashboard");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Robots.txt
|
// Robots.txt
|
||||||
app.get("/robots.txt", async (_request, response) => {
|
app.get("/robots.txt", async (_request, response) => {
|
||||||
let txt = "User-agent: *\nDisallow:";
|
let txt = "User-agent: *\nDisallow:";
|
||||||
|
@ -201,8 +201,13 @@ const getDaysRemaining = (validFrom, validTo) => {
|
|||||||
// param: info - the chain obtained from getPeerCertificate()
|
// param: info - the chain obtained from getPeerCertificate()
|
||||||
const parseCertificateInfo = function (info) {
|
const parseCertificateInfo = function (info) {
|
||||||
let link = info;
|
let link = info;
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
const existingList = {};
|
||||||
|
|
||||||
while (link) {
|
while (link) {
|
||||||
|
debug(`[${i}] ${link.fingerprint}`);
|
||||||
|
|
||||||
if (!link.valid_from || !link.valid_to) {
|
if (!link.valid_from || !link.valid_to) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -210,15 +215,24 @@ const parseCertificateInfo = function (info) {
|
|||||||
link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", ");
|
link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", ");
|
||||||
link.daysRemaining = getDaysRemaining(new Date(), link.validTo);
|
link.daysRemaining = getDaysRemaining(new Date(), link.validTo);
|
||||||
|
|
||||||
|
existingList[link.fingerprint] = true;
|
||||||
|
|
||||||
// Move up the chain until loop is encountered
|
// Move up the chain until loop is encountered
|
||||||
if (link.issuerCertificate == null) {
|
if (link.issuerCertificate == null) {
|
||||||
break;
|
break;
|
||||||
} else if (link.fingerprint == link.issuerCertificate.fingerprint) {
|
} else if (link.issuerCertificate.fingerprint in existingList) {
|
||||||
|
debug(`[Last] ${link.issuerCertificate.fingerprint}`);
|
||||||
link.issuerCertificate = null;
|
link.issuerCertificate = null;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
link = link.issuerCertificate;
|
link = link.issuerCertificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should be no use, but just in case.
|
||||||
|
if (i > 500) {
|
||||||
|
throw new Error("Dead loop occurred in parseCertificateInfo");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
@ -228,6 +242,7 @@ exports.checkCertificate = function (res) {
|
|||||||
const info = res.request.res.socket.getPeerCertificate(true);
|
const info = res.request.res.socket.getPeerCertificate(true);
|
||||||
const valid = res.request.res.socket.authorized || false;
|
const valid = res.request.res.socket.authorized || false;
|
||||||
|
|
||||||
|
debug("Parsing Certificate Info");
|
||||||
const parsedInfo = parseCertificateInfo(info);
|
const parsedInfo = parseCertificateInfo(info);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -189,7 +189,7 @@ textarea.form-control {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-hover > tbody > tr:hover {
|
.table-hover > tbody > tr:hover > * {
|
||||||
--bs-table-accent-bg: #070a10;
|
--bs-table-accent-bg: #070a10;
|
||||||
color: $dark-font-color;
|
color: $dark-font-color;
|
||||||
}
|
}
|
||||||
@ -346,6 +346,10 @@ textarea.form-control {
|
|||||||
&.active {
|
&.active {
|
||||||
background-color: #cdf8f4;
|
background-color: #cdf8f4;
|
||||||
}
|
}
|
||||||
|
.tags {
|
||||||
|
// Removes margin to line up tags list with uptime percentage
|
||||||
|
margin-left: -0.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@
|
|||||||
<Uptime :monitor="monitor.element" type="24" :pill="true" />
|
<Uptime :monitor="monitor.element" type="24" :pill="true" />
|
||||||
{{ monitor.element.name }}
|
{{ monitor.element.name }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tags">
|
||||||
|
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
||||||
<HeartbeatBar size="small" :monitor-id="monitor.element.id" />
|
<HeartbeatBar size="small" :monitor-id="monitor.element.id" />
|
||||||
@ -59,12 +62,14 @@
|
|||||||
import Draggable from "vuedraggable";
|
import Draggable from "vuedraggable";
|
||||||
import HeartbeatBar from "./HeartbeatBar.vue";
|
import HeartbeatBar from "./HeartbeatBar.vue";
|
||||||
import Uptime from "./Uptime.vue";
|
import Uptime from "./Uptime.vue";
|
||||||
|
import Tag from "./Tag.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Draggable,
|
Draggable,
|
||||||
HeartbeatBar,
|
HeartbeatBar,
|
||||||
Uptime,
|
Uptime,
|
||||||
|
Tag,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
editMode: {
|
editMode: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<span :class="className">{{ uptime }}</span>
|
<span :class="className" :title="24 + $t('-hour')">{{ uptime }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -77,7 +77,7 @@ export default {
|
|||||||
"Accepted Status Codes": "Допустими статус кодове",
|
"Accepted Status Codes": "Допустими статус кодове",
|
||||||
Save: "Запази",
|
Save: "Запази",
|
||||||
Notifications: "Известявания",
|
Notifications: "Известявания",
|
||||||
"Not available, please setup.": "Не е налично. Моля, настройте.",
|
"Not available, please setup.": "Не са налични. Моля, настройте.",
|
||||||
"Setup Notification": "Настройки за известявания",
|
"Setup Notification": "Настройки за известявания",
|
||||||
Light: "Светла",
|
Light: "Светла",
|
||||||
Dark: "Тъмна",
|
Dark: "Тъмна",
|
||||||
@ -141,7 +141,7 @@ export default {
|
|||||||
Overwrite: "Презапиши",
|
Overwrite: "Презапиши",
|
||||||
Options: "Опции",
|
Options: "Опции",
|
||||||
"Keep both": "Запази двете",
|
"Keep both": "Запази двете",
|
||||||
"Verify Token": "Проверка на токен код",
|
"Verify Token": "Провери токен код",
|
||||||
"Setup 2FA": "Настройка 2FA",
|
"Setup 2FA": "Настройка 2FA",
|
||||||
"Enable 2FA": "Включи 2FA",
|
"Enable 2FA": "Включи 2FA",
|
||||||
"Disable 2FA": "Изключи 2FA",
|
"Disable 2FA": "Изключи 2FA",
|
||||||
@ -298,8 +298,13 @@ export default {
|
|||||||
HeadersInvalidFormat: "Заявените хедъри не са валидни JSON: ",
|
HeadersInvalidFormat: "Заявените хедъри не са валидни JSON: ",
|
||||||
BodyInvalidFormat: "Заявеното съобщение не е валиден JSON: ",
|
BodyInvalidFormat: "Заявеното съобщение не е валиден JSON: ",
|
||||||
"Monitor History": "История на мониторите",
|
"Monitor History": "История на мониторите",
|
||||||
clearDataOlderThan: "Ще се съхранява за {0} дни.",
|
clearDataOlderThan: "Ще се съхранява {0} дни.",
|
||||||
records: "записа",
|
records: "записа",
|
||||||
"One record": "Един запис",
|
"One record": "Един запис",
|
||||||
steamApiKeyDescription: "За да мониторирате Steam Gameserver се нуждаете от Steam Web-API ключ. Може да регистрирате Вашия API ключ тук: ",
|
steamApiKeyDescription: "За да мониторирате Steam Gameserver се нуждаете от Steam Web-API ключ. Може да регистрирате Вашия API ключ тук: ",
|
||||||
|
clicksendsms: "ClickSend SMS",
|
||||||
|
apiCredentials: "API удостоверяване",
|
||||||
|
PasswordsDoNotMatch: "Паролите не съвпадат.",
|
||||||
|
"Current User": "Текущ потребител",
|
||||||
|
recent: "Скорошни",
|
||||||
};
|
};
|
||||||
|
@ -279,4 +279,29 @@ export default {
|
|||||||
promosmsTypeSpeed: "SMS SPEED - La plus haute des priorités dans le système. Très rapide et fiable mais cher (environ le double du prix d'un SMS FULL).",
|
promosmsTypeSpeed: "SMS SPEED - La plus haute des priorités dans le système. Très rapide et fiable mais cher (environ le double du prix d'un SMS FULL).",
|
||||||
promosmsPhoneNumber: "Numéro de téléphone (Poiur les déstinataires Polonais, vous pouvez enlever les codes interna.)",
|
promosmsPhoneNumber: "Numéro de téléphone (Poiur les déstinataires Polonais, vous pouvez enlever les codes interna.)",
|
||||||
promosmsSMSSender: "SMS Expéditeur : Nom pré-enregistré ou l'un de base: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
promosmsSMSSender: "SMS Expéditeur : Nom pré-enregistré ou l'un de base: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||||
|
"Primary Base URL": "Primary Base URL",
|
||||||
|
emailCustomSubject: "Sujet personalisé",
|
||||||
|
clicksendsms: "ClickSend SMS",
|
||||||
|
checkPrice: "Vérification {0} tarifs:",
|
||||||
|
apiCredentials: "Crédentials de l'API",
|
||||||
|
octopushLegacyHint: "Vous utilisez l'ancienne version d'Octopush (2011-2020) ou la nouvelle version ?",
|
||||||
|
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||||
|
matrixHomeserverURL: "L'URL du serveur (avec http(s):// et le port de manière facultatif)",
|
||||||
|
"Internal Room Id": "ID de la salle interne",
|
||||||
|
matrixDesc1: "Vous pouvez trouvez l'ID de salle interne en regardant dans la section avancée des paramètres dans le client Matrix. C'est sensé ressembler à: !QMdRCpUIfLwsfjxye6:home.server.",
|
||||||
|
matrixDesc2: "Il est fortement recommandé de créer un nouvel utilisateur et de ne pas utiliser le jeton d'accès de votre propre utilisateur Matrix, car il vous donnera un accès complet à votre compte et à toutes les salles que vous avez rejointes. Au lieu de cela, créez un nouvel utilisateur et invitez-le uniquement dans la salle dans laquelle vous souhaitez recevoir la notification. Vous pouvez obtenir le jeton d'accès en exécutant {0}",
|
||||||
|
Method: "Méthode",
|
||||||
|
Body: "Le corps",
|
||||||
|
Headers: "En-têtes",
|
||||||
|
PushUrl: "Push URL",
|
||||||
|
HeadersInvalidFormat: "L'en-têtes de la requête n'est pas dans un format JSON valide: ",
|
||||||
|
BodyInvalidFormat: "Le corps de la requête n'est pas dans un format JSON valide: ",
|
||||||
|
"Monitor History": "Historique de la sonde",
|
||||||
|
clearDataOlderThan: "Garder l'historique des données de la sonde durant {0} jours.",
|
||||||
|
PasswordsDoNotMatch: "Les mots de passe ne correspondent pas.",
|
||||||
|
records: "Enregistrements",
|
||||||
|
"One record": "Un enregistrement",
|
||||||
|
steamApiKeyDescription: "Pour surveiller un serveur Steam, vous avez besoin d'une clé Steam Web-API. Vous pouvez enregistrer votre clé ici: ",
|
||||||
|
"Current User": "Utilisateur actuel",
|
||||||
|
recent: "Récent",
|
||||||
};
|
};
|
||||||
|
@ -77,6 +77,17 @@
|
|||||||
<font-awesome-icon icon="save" />
|
<font-awesome-icon icon="save" />
|
||||||
{{ $t("Switch to Dark Theme") }}
|
{{ $t("Switch to Dark Theme") }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button class="btn btn-secondary me-2" @click="changeTagsVisibilty(!tagsVisible)">
|
||||||
|
<template v-if="tagsVisible">
|
||||||
|
<font-awesome-icon icon="eye-slash" />
|
||||||
|
{{ $t("Hide Tags") }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<font-awesome-icon icon="eye" />
|
||||||
|
{{ $t("Show Tags") }}
|
||||||
|
</template>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -292,6 +303,10 @@ export default {
|
|||||||
return this.config.statusPageTheme;
|
return this.config.statusPageTheme;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
tagsVisible() {
|
||||||
|
return this.config.statusPageTags
|
||||||
|
},
|
||||||
|
|
||||||
logoClass() {
|
logoClass() {
|
||||||
if (this.editMode) {
|
if (this.editMode) {
|
||||||
return {
|
return {
|
||||||
@ -472,6 +487,25 @@ export default {
|
|||||||
changeTheme(name) {
|
changeTheme(name) {
|
||||||
this.config.statusPageTheme = name;
|
this.config.statusPageTheme = name;
|
||||||
},
|
},
|
||||||
|
changeTagsVisibilty(newState) {
|
||||||
|
this.config.statusPageTags = newState;
|
||||||
|
|
||||||
|
// On load, the status page will not include tags if it's not enabled for security reasons
|
||||||
|
// Which means if we enable tags, it won't show in the UI until saved
|
||||||
|
// So we have this to enhance UX and load in the tags from the authenticated source instantly
|
||||||
|
this.$root.publicGroupList = this.$root.publicGroupList.map((group) => {
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
monitorList: group.monitorList.map((monitor) => {
|
||||||
|
// We only include the tags if visible so we can reuse the logic to hide the tags on disable
|
||||||
|
return {
|
||||||
|
...monitor,
|
||||||
|
tags: newState ? this.$root.monitorList[monitor.id].tags : []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crop Success
|
* Crop Success
|
||||||
|
@ -1,4 +1,125 @@
|
|||||||
const { genSecret, sleep } = require("../src/util");
|
const { genSecret, sleep } = require("../src/util");
|
||||||
|
const utilServerRewire = require("../server/util-server");
|
||||||
|
|
||||||
|
describe("Test parseCertificateInfo", () => {
|
||||||
|
it("should handle undefined", async () => {
|
||||||
|
const parseCertificateInfo = utilServerRewire.__get__("parseCertificateInfo");
|
||||||
|
const info = parseCertificateInfo(undefined);
|
||||||
|
expect(info).toEqual(undefined);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
it("should handle normal cert chain", async () => {
|
||||||
|
const parseCertificateInfo = utilServerRewire.__get__("parseCertificateInfo");
|
||||||
|
|
||||||
|
const chain1 = {
|
||||||
|
fingerprint: "CF:2C:F3:6A:FE:6B:10:EC:44:77:C8:95:BB:96:2E:06:1F:0E:15:DA",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain2 = {
|
||||||
|
fingerprint: "A0:31:C4:67:82:E6:E6:C6:62:C2:C8:7C:76:DA:9A:A6:2C:CA:BD:8E",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain3 = {
|
||||||
|
fingerprint: "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
chain1.issuerCertificate = chain2;
|
||||||
|
chain2.issuerCertificate = chain3;
|
||||||
|
chain3.issuerCertificate = chain3;
|
||||||
|
|
||||||
|
const info = parseCertificateInfo(chain1);
|
||||||
|
expect(chain1).toEqual(info);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
it("should handle cert chain with strange circle", async () => {
|
||||||
|
const parseCertificateInfo = utilServerRewire.__get__("parseCertificateInfo");
|
||||||
|
|
||||||
|
const chain1 = {
|
||||||
|
fingerprint: "CF:2C:F3:6A:FE:6B:10:EC:44:77:C8:95:BB:96:2E:06:1F:0E:15:DA",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain2 = {
|
||||||
|
fingerprint: "A0:31:C4:67:82:E6:E6:C6:62:C2:C8:7C:76:DA:9A:A6:2C:CA:BD:8E",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain3 = {
|
||||||
|
fingerprint: "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain4 = {
|
||||||
|
fingerprint: "haha",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
chain1.issuerCertificate = chain2;
|
||||||
|
chain2.issuerCertificate = chain3;
|
||||||
|
chain3.issuerCertificate = chain4;
|
||||||
|
chain4.issuerCertificate = chain2;
|
||||||
|
|
||||||
|
const info = parseCertificateInfo(chain1);
|
||||||
|
expect(chain1).toEqual(info);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
it("should handle cert chain with last undefined (should be happen in real, but just in case)", async () => {
|
||||||
|
const parseCertificateInfo = utilServerRewire.__get__("parseCertificateInfo");
|
||||||
|
|
||||||
|
const chain1 = {
|
||||||
|
fingerprint: "CF:2C:F3:6A:FE:6B:10:EC:44:77:C8:95:BB:96:2E:06:1F:0E:15:DA",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain2 = {
|
||||||
|
fingerprint: "A0:31:C4:67:82:E6:E6:C6:62:C2:C8:7C:76:DA:9A:A6:2C:CA:BD:8E",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain3 = {
|
||||||
|
fingerprint: "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
const chain4 = {
|
||||||
|
fingerprint: "haha",
|
||||||
|
valid_from: "Oct 22 12:00:00 2013 GMT",
|
||||||
|
valid_to: "Oct 22 12:00:00 2028 GMT",
|
||||||
|
subjectaltname: "DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net",
|
||||||
|
};
|
||||||
|
|
||||||
|
chain1.issuerCertificate = chain2;
|
||||||
|
chain2.issuerCertificate = chain3;
|
||||||
|
chain3.issuerCertificate = chain4;
|
||||||
|
chain4.issuerCertificate = undefined;
|
||||||
|
|
||||||
|
const info = parseCertificateInfo(chain1);
|
||||||
|
expect(chain1).toEqual(info);
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
describe("Test genSecret", () => {
|
describe("Test genSecret", () => {
|
||||||
|
|
||||||
@ -42,3 +163,4 @@ describe("Test reset-password", () => {
|
|||||||
await require("../extra/reset-password").main();
|
await require("../extra/reset-password").main();
|
||||||
}, 120000);
|
}, 120000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user