Compare commits

...

15 Commits

Author SHA1 Message Date
Louis Lam
121ab82cc3
Merge pull request #2714 from Saibamen/auto_test_ignore_markdown
Do not run auto-test for markdown-only commits. Update versions
2023-02-03 17:41:17 +08:00
Adam Stachowicz
e0f0178644 Do not run auto-test for markdown-only commits. Update versions 2023-02-03 07:10:10 +01:00
Louis Lam
ec78d2a39b
Update README.md 2023-02-03 13:39:45 +08:00
Louis Lam
ff09276de2
Update README.md 2023-02-03 13:38:14 +08:00
Louis Lam
e631db89b8 Update to 1.20.0-beta.0 2023-02-03 13:21:19 +08:00
Louis Lam
d39508a007 Change nightly version format 2023-02-03 13:19:51 +08:00
Louis Lam
b0673ba9ce
Merge pull request #2570 from Computroniks/feature/#2365-allow-markdown-in-status-page-footer
Add support for markdown on status page
2023-02-03 12:36:50 +08:00
Joseph
2a6d98ff01
Feat: Expand and Simplify Badge Functionality (#2211)
* [expanding badges] added new configs

* [expanding badges] recieve ping in getPreviousHeartbeat()

* [expanding badges] re-added original new badges

* [expanding badges] recreate parity between old and new badges

* [expanding badges] fix linting
2023-02-03 12:33:48 +08:00
Matthew Nickson
3819266fa8
Fixed style of links in markdown
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2023-02-02 17:55:40 +00:00
Louis Lam
a8f0f1d872 Merge manually and remove to devDependencies 2023-02-01 15:51:33 +08:00
Louis Lam
f82d7b4007 Merge remote-tracking branch 'origin/master' into feature/#2365-allow-markdown-in-status-page-footer
# Conflicts:
#	package-lock.json
#	package.json
#	src/languages/en.js
2023-02-01 15:38:33 +08:00
Matthew Nickson
6e50784b6b
Merge branch 'master' into feature/#2365-allow-markdown-in-status-page-footer 2023-01-09 20:03:11 +00:00
Matthew Nickson
80f2d6e2a7
Added markdown support for maintenance
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2023-01-08 20:54:16 +00:00
Matthew Nickson
852a088529
Added mardown support for incident
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2023-01-08 20:46:18 +00:00
Matthew Nickson
6bc0bd84af
Allowed markdown in footer of status page
Markdown support has been added using the marked module. To secure
against XSS attacks, DOMPurify is used to sanitize the generated HTML
before it is loaded on the page.

Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2023-01-08 20:39:27 +00:00
14 changed files with 342 additions and 19 deletions

View File

@ -6,8 +6,12 @@ name: Auto Test
on:
push:
branches: [ master ]
paths-ignore:
- '*.md'
pull_request:
branches: [ master ]
paths-ignore:
- '*.md'
jobs:
auto-test:
@ -36,6 +40,7 @@ jobs:
env:
HEADLESS_TEST: 1
JUST_FOR_TEST: ${{ secrets.JUST_FOR_TEST }}
check-linters:
runs-on: ubuntu-latest

View File

@ -1,4 +1,3 @@
name: Close Incorrect Issue
on:
@ -12,13 +11,13 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node-version: [16.x]
node-version: [16]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

View File

@ -3,13 +3,13 @@ on:
workflow_dispatch:
schedule:
- cron: '0 */6 * * *'
#Run every 6 hours
#Run every 6 hours
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v7
with:
stale-issue-message: 'We are clearing up our old issues and your ticket has been open for 3 months with no activity. Remove stale label or comment or this will be closed in 2 days.'
close-issue-message: 'This issue was closed because it has been stalled for 2 days with no activity.'

View File

@ -1,7 +1,7 @@
# Uptime Kuma
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Open%20Collective%20Backers&color=brightgreen" /></a>
[![GitHub Sponsors](https://img.shields.io/github/sponsors/louislam?label=GitHub%20Sponsors)](https://github.com/sponsors/louislam) <a href="https://weblate.kuma.pet/engage/uptime-kuma/">
[![GitHub Sponsors](https://img.shields.io/github/sponsors/louislam?label=GitHub%20Sponsors)](https://github.com/sponsors/louislam) <a href="https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/">
<img src="https://weblate.kuma.pet/widgets/uptime-kuma/-/svg-badge.svg" alt="Translation status" />
</a>
@ -18,7 +18,6 @@ Uptime Kuma is an easy-to-use self-hosted monitoring tool.
Try it!
- Tokyo Demo Server: https://demo.uptime.kuma.pet (Sponsored by [Uptime Kuma Sponsors](https://github.com/louislam/uptime-kuma#%EF%B8%8F-sponsors))
- Europe Demo Server: https://demo.uptime-kuma.karimi.dev:27000 (Provided by [@mhkarimi1383](https://github.com/mhkarimi1383))
It is a temporary live demo, all data will be deleted after 10 minutes. Use the one that is closer to you, but I suggest that you should install and try it out for the best demo experience.

View File

@ -1,11 +1,12 @@
const pkg = require("../package.json");
const fs = require("fs");
const util = require("../src/util");
const dayjs = require("dayjs");
util.polyfill();
const oldVersion = pkg.version;
const newVersion = oldVersion + "-nightly-" + util.genSecret(8);
const newVersion = oldVersion + "-nightly-" + dayjs().format("YYYYMMDDHHmmss");
console.log("Old Version: " + oldVersion);
console.log("New Version: " + newVersion);

32
package-lock.json generated
View File

@ -89,10 +89,12 @@
"cypress": "^10.1.0",
"delay": "^5.0.0",
"dns2": "~2.0.1",
"dompurify": "~2.4.3",
"eslint": "~8.14.0",
"eslint-plugin-vue": "~8.7.1",
"favico.js": "~0.3.10",
"jest": "~27.2.5",
"marked": "~4.2.5",
"postcss-html": "~1.5.0",
"postcss-rtlcss": "~3.7.2",
"postcss-scss": "~4.0.4",
@ -7801,6 +7803,12 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==",
"dev": true
},
"node_modules/domutils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
@ -13622,6 +13630,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/marked": {
"version": "4.2.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz",
"integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==",
"dev": true,
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/mathml-tag-names": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
@ -24880,6 +24900,12 @@
"domelementtype": "^2.3.0"
}
},
"dompurify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==",
"dev": true
},
"domutils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
@ -29114,6 +29140,12 @@
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"dev": true
},
"marked": {
"version": "4.2.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz",
"integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==",
"dev": true
},
"mathml-tag-names": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "uptime-kuma",
"version": "1.19.6",
"version": "1.20.0-beta.0",
"license": "MIT",
"repository": {
"type": "git",
@ -146,9 +146,11 @@
"cypress": "^10.1.0",
"delay": "^5.0.0",
"dns2": "~2.0.1",
"dompurify": "~2.4.3",
"eslint": "~8.14.0",
"eslint-plugin-vue": "~8.7.1",
"favico.js": "~0.3.10",
"marked": "~4.2.5",
"jest": "~27.2.5",
"postcss-html": "~1.5.0",
"postcss-rtlcss": "~3.7.2",

View File

@ -4,6 +4,7 @@ const demoMode = args["demo"] || false;
const badgeConstants = {
naColor: "#999",
defaultUpColor: "#66c20a",
defaultWarnColor: "#eed202",
defaultDownColor: "#c2290a",
defaultPendingColor: "#f8a306",
defaultMaintenanceColor: "#1747f5",
@ -13,6 +14,11 @@ const badgeConstants = {
defaultPingLabelSuffix: "h",
defaultUptimeValueSuffix: "%",
defaultUptimeLabelSuffix: "h",
defaultCertExpValueSuffix: " days",
defaultCertExpLabelSuffix: "h",
// Values Come From Default Notification Times
defaultCertExpireWarnDays: "14",
defaultCertExpireDownDays: "7"
};
module.exports = {

View File

@ -1255,7 +1255,7 @@ class Monitor extends BeanModel {
*/
static async getPreviousHeartbeat(monitorID) {
return await R.getRow(`
SELECT status, time FROM heartbeat
SELECT ping, status, time FROM heartbeat
WHERE id = (select MAX(id) from heartbeat where monitor_id = ?)
`, [
monitorID

View File

@ -145,7 +145,7 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response
const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId);
const state = overrideValue !== undefined ? overrideValue : heartbeat.status;
badgeValues.label = label ?? "";
badgeValues.label = label ?? "Status";
switch (state) {
case DOWN:
badgeValues.color = downColor;
@ -212,7 +212,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
const badgeValues = { style };
if (!publicMonitor) {
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non existent
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
@ -228,8 +228,11 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
badgeValues.color = color ?? percentageToColor(uptime);
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
badgeValues.labelColor = labelColor ?? "";
// build a lable string. If a custom label is given, override the default one (requestedDuration)
badgeValues.label = filterAndJoin([ labelPrefix, label ?? requestedDuration, labelSuffix ]);
// build a label string. If a custom label is given, override the default one (requestedDuration)
badgeValues.label = filterAndJoin([
labelPrefix,
label ?? `Uptime (${requestedDuration}${labelSuffix})`,
]);
badgeValues.message = filterAndJoin([ prefix, `${cleanUptime * 100}`, suffix ]);
}
@ -290,7 +293,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request,
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
badgeValues.labelColor = labelColor ?? "";
// build a lable string. If a custom label is given, override the default one (requestedDuration)
badgeValues.label = filterAndJoin([ labelPrefix, label ?? requestedDuration, labelSuffix ]);
badgeValues.label = filterAndJoin([ labelPrefix, label ?? `Avg. Ping (${requestedDuration}${labelSuffix})` ]);
badgeValues.message = filterAndJoin([ prefix, avgPing, suffix ]);
}
@ -304,4 +307,237 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request,
}
});
router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (request, response) => {
allowAllOrigin(response);
const {
label,
labelPrefix,
labelSuffix,
prefix,
suffix = badgeConstants.defaultPingValueSuffix,
color = badgeConstants.defaultPingColor,
labelColor,
style = badgeConstants.defaultStyle,
value, // for demo purpose only
} = request.query;
try {
const requestedMonitorId = parseInt(request.params.id, 10);
// Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d)
const requestedDuration = Math.min(
request.params.duration
? parseInt(request.params.duration, 10)
: 24,
720
);
const overrideValue = value && parseFloat(value);
const publicAvgPing = parseInt(await R.getCell(`
SELECT AVG(ping) FROM monitor_group, \`group\`, heartbeat
WHERE monitor_group.group_id = \`group\`.id
AND heartbeat.time > DATETIME('now', ? || ' hours')
AND heartbeat.ping IS NOT NULL
AND public = 1
AND heartbeat.monitor_id = ?
`,
[ -requestedDuration, requestedMonitorId ]
));
const badgeValues = { style };
if (!publicAvgPing) {
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non existent
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
const avgPing = parseInt(overrideValue ?? publicAvgPing);
badgeValues.color = color;
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
badgeValues.labelColor = labelColor ?? "";
// build a label string. If a custom label is given, override the default one (requestedDuration)
badgeValues.label = filterAndJoin([
labelPrefix,
label ?? `Avg. Response (${requestedDuration}h)`,
labelSuffix,
]);
badgeValues.message = filterAndJoin([ prefix, avgPing, suffix ]);
}
// build the SVG based on given values
const svg = makeBadge(badgeValues);
response.type("image/svg+xml");
response.send(svg);
} catch (error) {
send403(response, error.message);
}
});
router.get("/api/badge/:id/cert-exp", cache("5 minutes"), async (request, response) => {
allowAllOrigin(response);
const date = request.query.date;
const {
label,
labelPrefix,
labelSuffix,
prefix,
suffix = date ? "" : badgeConstants.defaultCertExpValueSuffix,
upColor = badgeConstants.defaultUpColor,
warnColor = badgeConstants.defaultWarnColor,
downColor = badgeConstants.defaultDownColor,
warnDays = badgeConstants.defaultCertExpireWarnDays,
downDays = badgeConstants.defaultCertExpireDownDays,
labelColor,
style = badgeConstants.defaultStyle,
value, // for demo purpose only
} = request.query;
try {
const requestedMonitorId = parseInt(request.params.id, 10);
const overrideValue = value && parseFloat(value);
let publicMonitor = await R.getRow(`
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
WHERE monitor_group.group_id = \`group\`.id
AND monitor_group.monitor_id = ?
AND public = 1
`,
[ requestedMonitorId ]
);
const badgeValues = { style };
if (!publicMonitor) {
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non existent
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
const tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
requestedMonitorId,
]);
if (!tlsInfoBean) {
// return a "No/Bad Cert" badge in naColor (grey), if no cert saved (does not save bad certs?)
badgeValues.message = "No/Bad Cert";
badgeValues.color = badgeConstants.naColor;
} else {
const tlsInfo = JSON.parse(tlsInfoBean.info_json);
if (!tlsInfo.valid) {
// return a "Bad Cert" badge in naColor (grey), when cert is not valid
badgeValues.message = "Bad Cert";
badgeValues.color = badgeConstants.downColor;
} else {
const daysRemaining = parseInt(overrideValue ?? tlsInfo.certInfo.daysRemaining);
if (daysRemaining > warnDays) {
badgeValues.color = upColor;
} else if (daysRemaining > downDays) {
badgeValues.color = warnColor;
} else {
badgeValues.color = downColor;
}
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
badgeValues.labelColor = labelColor ?? "";
// build a label string. If a custom label is given, override the default one
badgeValues.label = filterAndJoin([
labelPrefix,
label ?? "Cert Exp.",
labelSuffix,
]);
badgeValues.message = filterAndJoin([ prefix, date ? tlsInfo.certInfo.validTo : daysRemaining, suffix ]);
}
}
}
// build the SVG based on given values
const svg = makeBadge(badgeValues);
response.type("image/svg+xml");
response.send(svg);
} catch (error) {
send403(response, error.message);
}
});
router.get("/api/badge/:id/response", cache("5 minutes"), async (request, response) => {
allowAllOrigin(response);
const {
label,
labelPrefix,
labelSuffix,
prefix,
suffix = badgeConstants.defaultPingValueSuffix,
color = badgeConstants.defaultPingColor,
labelColor,
style = badgeConstants.defaultStyle,
value, // for demo purpose only
} = request.query;
try {
const requestedMonitorId = parseInt(request.params.id, 10);
const overrideValue = value && parseFloat(value);
let publicMonitor = await R.getRow(`
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
WHERE monitor_group.group_id = \`group\`.id
AND monitor_group.monitor_id = ?
AND public = 1
`,
[ requestedMonitorId ]
);
const badgeValues = { style };
if (!publicMonitor) {
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non existent
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
const heartbeat = await Monitor.getPreviousHeartbeat(
requestedMonitorId
);
if (!heartbeat.ping) {
// return a "N/A" badge in naColor (grey), if previous heartbeat has no ping
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
const ping = parseInt(overrideValue ?? heartbeat.ping);
badgeValues.color = color;
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
badgeValues.labelColor = labelColor ?? "";
// build a label string. If a custom label is given, override the default one
badgeValues.label = filterAndJoin([
labelPrefix,
label ?? "Response",
labelSuffix,
]);
badgeValues.message = filterAndJoin([ prefix, ping, suffix ]);
}
}
// build the SVG based on given values
const svg = makeBadge(badgeValues);
response.type("image/svg+xml");
response.send(svg);
} catch (error) {
send403(response, error.message);
}
});
module.exports = router;

View File

@ -35,6 +35,11 @@ textarea.form-control {
color: $maintenance !important;
}
.incident a,
.bg-maintenance a {
color: inherit;
}
.list-group {
border-radius: 0.75rem;
@ -248,6 +253,11 @@ optgroup {
}
}
.incident a,
.bg-maintenance a {
color: inherit;
}
.form-control,
.form-control:focus,
.form-select,

View File

@ -25,6 +25,7 @@
"General Monitor Type": "General Monitor Type",
"Passive Monitor Type": "Passive Monitor Type",
"Specific Monitor Type": "Specific Monitor Type",
"markdownSupported": "Markdown syntax supported",
"pauseDashboardHome": "Pause",
"Pause": "Pause",
"Name": "Name",

View File

@ -21,6 +21,9 @@
<textarea
id="description" v-model="maintenance.description" class="form-control"
></textarea>
<div class="form-text">
{{ $t("markdownSupported") }}
</div>
</div>
<!-- Affected Monitors -->

View File

@ -26,6 +26,9 @@
<div class="my-3">
<label for="footer-text" class="form-label">{{ $t("Footer Text") }}</label>
<textarea id="footer-text" v-model="config.footerText" class="form-control"></textarea>
<div class="form-text">
{{ $t("markdownSupported") }}
</div>
</div>
<div class="my-3 form-check form-switch">
@ -148,7 +151,12 @@
<Editable v-model="incident.title" tag="h4" :contenteditable="editIncidentMode" :noNL="true" class="alert-heading" />
<strong v-if="editIncidentMode">{{ $t("Content") }}:</strong>
<Editable v-model="incident.content" tag="div" :contenteditable="editIncidentMode" class="content" />
<Editable v-if="editIncidentMode" v-model="incident.content" tag="div" :contenteditable="editIncidentMode" class="content" />
<div v-if="editIncidentMode" class="form-text">
{{ $t("markdownSupported") }}
</div>
<!-- eslint-disable-next-line vue/no-v-html-->
<div v-if="! editIncidentMode" class="content" v-html="incidentHTML"></div>
<!-- Incident Date -->
<div class="date mt-3">
@ -236,7 +244,8 @@
class="shadow-box alert mb-4 p-3 bg-maintenance mt-4 position-relative" role="alert"
>
<h4 class="alert-heading">{{ maintenance.title }}</h4>
<div class="content">{{ maintenance.description }}</div>
<!-- eslint-disable-next-line vue/no-v-html-->
<div class="content" v-html="maintenanceHTML(maintenance.description)"></div>
<MaintenanceTime :maintenance="maintenance" />
</div>
</template>
@ -279,7 +288,9 @@
<div class="custom-footer-text text-start">
<strong v-if="enableEditMode">{{ $t("Custom Footer") }}:</strong>
</div>
<Editable v-model="config.footerText" tag="div" :contenteditable="enableEditMode" :noNL="false" class="alert-heading p-2" />
<Editable v-if="enableEditMode" v-model="config.footerText" tag="div" :contenteditable="enableEditMode" :noNL="false" class="alert-heading p-2" />
<!-- eslint-disable-next-line vue/no-v-html-->
<div v-if="! enableEditMode" class="alert-heading p-2" v-html="footerHTML"></div>
<p v-if="config.showPoweredBy">
{{ $t("Powered by") }} <a target="_blank" rel="noopener noreferrer" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
@ -310,6 +321,8 @@ import ImageCropUpload from "vue-image-crop-upload";
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
import { useToast } from "vue-toastification";
import { marked } from "marked";
import DOMPurify from "dompurify";
import Confirm from "../components/Confirm.vue";
import PublicGroupList from "../components/PublicGroupList.vue";
import MaintenanceTime from "../components/MaintenanceTime.vue";
@ -477,6 +490,13 @@ export default {
return this.overallStatus === STATUS_PAGE_MAINTENANCE;
},
incidentHTML() {
return DOMPurify.sanitize(marked(this.incident.content));
},
footerHTML() {
return DOMPurify.sanitize(marked(this.config.footerText));
},
},
watch: {
@ -836,6 +856,15 @@ export default {
this.config.domainNameList.splice(index, 1);
},
/**
* Generate sanitized HTML from maintenance description
* @param {string} description
* @returns {string} Sanitized HTML
*/
maintenanceHTML(description) {
return DOMPurify.sanitize(marked(description));
},
}
};
</script>