Merge branch 'master' into master

This commit is contained in:
Moritz R 2022-07-24 14:37:22 +02:00 committed by GitHub
commit 60e7824ff0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
104 changed files with 6172 additions and 8522 deletions

View file

@ -8,6 +8,7 @@
"declaration-empty-line-before": null, "declaration-empty-line-before": null,
"alpha-value-notation": "number", "alpha-value-notation": "number",
"color-function-notation": "legacy", "color-function-notation": "legacy",
"shorthand-property-no-redundant-values": null "shorthand-property-no-redundant-values": null,
"color-hex-length": null,
} }
} }

View file

@ -86,8 +86,8 @@ I personally do not like something need to learn so much and need to config so m
## Name convention ## Name convention
- Javascript/Typescript: camelCaseType - Javascript/Typescript: camelCaseType
- SQLite: underscore_type - SQLite: snake_case (Underscore)
- CSS/SCSS: dash-type - CSS/SCSS: kebab-case (Dash)
## Tools ## Tools

View file

@ -151,9 +151,9 @@ You can discuss or ask for help in [issues](https://github.com/louislam/uptime-k
### Subreddit ### Subreddit
My Reddit account: louislamlam My Reddit account: [u/louislamlam](https://reddit.com/u/louislamlam).
You can mention me if you ask a question on Reddit. You can mention me if you ask a question on Reddit.
https://www.reddit.com/r/UptimeKuma/ [r/Uptime kuma](https://www.reddit.com/r/UptimeKuma/)
## Contribute ## Contribute

View file

@ -11,11 +11,13 @@ const viteCompressionFilter = /\.(js|mjs|json|css|html|svg)$/i;
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
define: {
"FRONTEND_VERSION": JSON.stringify(process.env.npm_package_version),
},
plugins: [ plugins: [
vue(), vue(),
legacy({ legacy({
targets: [ "ie > 11" ], targets: [ "since 2015" ],
additionalLegacyPolyfills: [ "regenerator-runtime/runtime" ]
}), }),
visualizer({ visualizer({
filename: "tmp/dist-stats.html" filename: "tmp/dist-stats.html"

View file

@ -0,0 +1,5 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE monitor_group
ADD send_url BOOLEAN DEFAULT 0 NOT NULL;
COMMIT;

View file

@ -4,5 +4,5 @@ WORKDIR /app
# Install apprise, iputils for non-root ping, setpriv # Install apprise, iputils for non-root ping, setpriv
RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \ RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \
pip3 --no-cache-dir install apprise==0.9.8.3 && \ pip3 --no-cache-dir install apprise==0.9.9 && \
rm -rf /root/.cache rm -rf /root/.cache

View file

@ -11,7 +11,7 @@ WORKDIR /app
RUN apt update && \ RUN apt update && \
apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \ apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \
sqlite3 iputils-ping util-linux dumb-init && \ sqlite3 iputils-ping util-linux dumb-init && \
pip3 --no-cache-dir install apprise==0.9.8.3 && \ pip3 --no-cache-dir install apprise==0.9.9 && \
rm -rf /var/lib/apt/lists/* && \ rm -rf /var/lib/apt/lists/* && \
apt --yes autoremove apt --yes autoremove

View file

@ -1,4 +1,4 @@
# Simple docker-composer.yml # Simple docker-compose.yml
# You can change your port or volume location # You can change your port or volume location
version: '3.3' version: '3.3'

View file

@ -41,7 +41,7 @@ function updateWiki(newVersion) {
function safeDelete(dir) { function safeDelete(dir) {
if (fs.existsSync(dir)) { if (fs.existsSync(dir)) {
fs.rmdirSync(dir, { fs.rm(dir, {
recursive: true, recursive: true,
}); });
} }

11994
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.17.0-beta.0", "version": "1.17.1",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@ -39,7 +39,7 @@
"build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"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": "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",
"setup": "git checkout 1.16.1 && npm ci --production && npm run download-dist", "setup": "git checkout 1.17.1 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js", "download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",
@ -61,23 +61,15 @@
"build-dist-and-restart": "npm run build && npm run start-server-dev" "build-dist-and-restart": "npm run build && npm run start-server-dev"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "~1.2.36",
"@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-5",
"@louislam/sqlite3": "~15.0.6", "@louislam/sqlite3": "~15.0.6",
"@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0", "args-parser": "~1.3.0",
"axios": "~0.26.1", "axios": "~0.26.1",
"axios-cached-dns-resolve": "^3.0.6",
"axios-ntlm": "^1.3.0", "axios-ntlm": "^1.3.0",
"badge-maker": "^3.3.1", "badge-maker": "^3.3.1",
"bcryptjs": "~2.4.3", "bcryptjs": "~2.4.3",
"bootstrap": "5.1.3",
"bree": "~7.1.5", "bree": "~7.1.5",
"cacheable-lookup": "~6.0.4",
"chardet": "^1.3.0", "chardet": "^1.3.0",
"chart.js": "~3.6.2",
"chartjs-adapter-dayjs": "~1.0.0",
"check-password-strength": "^2.0.5", "check-password-strength": "^2.0.5",
"cheerio": "^1.0.0-rc.10", "cheerio": "^1.0.0-rc.10",
"chroma-js": "^2.1.2", "chroma-js": "^2.1.2",
@ -85,11 +77,9 @@
"compare-versions": "~3.6.0", "compare-versions": "~3.6.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"dayjs": "^1.11.0", "dayjs": "^1.11.0",
"esm-wallaby": "^3.2.26",
"express": "~4.17.3", "express": "~4.17.3",
"express-basic-auth": "~1.2.1", "express-basic-auth": "~1.2.1",
"express-static-gzip": "^2.1.7", "express-static-gzip": "^2.1.7",
"favico.js": "^0.3.10",
"form-data": "~4.0.0", "form-data": "~4.0.0",
"http-graceful-shutdown": "~3.1.7", "http-graceful-shutdown": "~3.1.7",
"http-proxy-agent": "^5.0.0", "http-proxy-agent": "^5.0.0",
@ -104,21 +94,60 @@
"nodemailer": "~6.6.5", "nodemailer": "~6.6.5",
"notp": "~2.0.3", "notp": "~2.0.3",
"password-hash": "~1.2.2", "password-hash": "~1.2.2",
"postcss-rtlcss": "~3.4.1", "pg": "^8.7.3",
"postcss-scss": "~4.0.3", "pg-connection-string": "^2.5.0",
"prismjs": "^1.27.0",
"prom-client": "~13.2.0", "prom-client": "~13.2.0",
"prometheus-api-metrics": "~3.2.1", "prometheus-api-metrics": "~3.2.1",
"qrcode": "~1.5.0",
"redbean-node": "0.1.4", "redbean-node": "0.1.4",
"socket.io": "~4.4.1", "socket.io": "~4.4.1",
"socket.io-client": "~4.4.1", "socket.io-client": "~4.4.1",
"socks-proxy-agent": "^6.1.1", "socks-proxy-agent": "6.1.1",
"tar": "^6.1.11", "tar": "^6.1.11",
"tcp-ping": "~0.1.1", "tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2", "thirty-two": "~1.0.2"
},
"devDependencies": {
"@actions/github": "~5.0.1",
"@babel/eslint-parser": "~7.17.0",
"@babel/preset-env": "^7.15.8",
"@fortawesome/fontawesome-svg-core": "~1.2.36",
"@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-5",
"@popperjs/core": "~2.10.2",
"@types/bootstrap": "~5.1.9",
"@vitejs/plugin-legacy": "~1.8.2",
"@vitejs/plugin-vue": "~2.3.3",
"@vue/compiler-sfc": "~3.2.36",
"aedes": "^0.46.3",
"babel-plugin-rewire": "~1.2.0",
"bootstrap": "5.1.3",
"chart.js": "~3.6.2",
"chartjs-adapter-dayjs": "~1.0.0",
"concurrently": "^7.1.0",
"core-js": "~3.18.3",
"cross-env": "~7.0.3",
"dns2": "~2.0.1",
"eslint": "~8.14.0",
"eslint-plugin-vue": "~8.7.1",
"favico.js": "^0.3.10",
"jest": "~27.2.5",
"jest-puppeteer": "~6.0.3",
"postcss-html": "^1.3.1",
"postcss-rtlcss": "~3.4.1",
"postcss-scss": "~4.0.3",
"prismjs": "^1.27.0",
"puppeteer": "~13.1.3",
"qrcode": "~1.5.0",
"rollup-plugin-visualizer": "^5.6.0",
"sass": "~1.42.1",
"stylelint": "~14.7.1",
"stylelint-config-standard": "~25.0.0",
"timezones-list": "~3.0.1", "timezones-list": "~3.0.1",
"typescript": "~4.4.4",
"v-pagination-3": "~0.1.7", "v-pagination-3": "~0.1.7",
"vite": "~2.9.9",
"vite-plugin-compression": "^0.5.1",
"vue": "next", "vue": "next",
"vue-chart-3": "3.0.9", "vue-chart-3": "3.0.9",
"vue-confirm-dialog": "~1.0.2", "vue-confirm-dialog": "~1.0.2",
@ -130,38 +159,7 @@
"vue-qrcode": "~1.0.0", "vue-qrcode": "~1.0.0",
"vue-router": "~4.0.14", "vue-router": "~4.0.14",
"vue-toastification": "~2.0.0-rc.5", "vue-toastification": "~2.0.0-rc.5",
"vuedraggable": "~4.1.0" "vuedraggable": "~4.1.0",
},
"devDependencies": {
"@actions/github": "~5.0.1",
"@babel/eslint-parser": "~7.17.0",
"@babel/preset-env": "^7.15.8",
"@types/bootstrap": "~5.1.9",
"@vitejs/plugin-legacy": "~1.8.2",
"@vitejs/plugin-vue": "~2.3.3",
"@vue/compiler-sfc": "~3.2.36",
"aedes": "^0.46.3",
"babel-plugin-rewire": "~1.2.0",
"concurrently": "^7.1.0",
"core-js": "~3.18.3",
"cross-env": "~7.0.3",
"delay": "^5.0.0",
"dns2": "~2.0.1",
"eslint": "~8.14.0",
"eslint-plugin-vue": "~8.7.1",
"jest": "~27.2.5",
"jest-puppeteer": "~6.0.3",
"lru-cache": "^7.7.1",
"npm-check-updates": "^12.5.9",
"postcss-html": "^1.3.1",
"puppeteer": "~13.1.3",
"rollup-plugin-visualizer": "^5.6.0",
"sass": "~1.42.1",
"stylelint": "~14.7.1",
"stylelint-config-standard": "~25.0.0",
"typescript": "~4.4.4",
"vite": "~2.9.9",
"vite-plugin-compression": "^0.5.1",
"wait-on": "^6.0.1" "wait-on": "^6.0.1"
} }
} }

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 893 B

View file

@ -0,0 +1,54 @@
const https = require("https");
const http = require("http");
const CacheableLookup = require("cacheable-lookup");
class CacheableDnsHttpAgent {
static cacheable = new CacheableLookup();
static httpAgentList = {};
static httpsAgentList = {};
/**
* Register cacheable to global agents
*/
static registerGlobalAgent() {
this.cacheable.install(http.globalAgent);
this.cacheable.install(https.globalAgent);
}
static install(agent) {
this.cacheable.install(agent);
}
/**
* @var {https.AgentOptions} agentOptions
* @return {https.Agent}
*/
static getHttpsAgent(agentOptions) {
let key = JSON.stringify(agentOptions);
if (!(key in this.httpsAgentList)) {
this.httpsAgentList[key] = new https.Agent(agentOptions);
this.cacheable.install(this.httpsAgentList[key]);
}
return this.httpsAgentList[key];
}
/**
* @var {http.AgentOptions} agentOptions
* @return {https.Agents}
*/
static getHttpAgent(agentOptions) {
let key = JSON.stringify(agentOptions);
if (!(key in this.httpAgentList)) {
this.httpAgentList[key] = new http.Agent(agentOptions);
this.cacheable.install(this.httpAgentList[key]);
}
return this.httpAgentList[key];
}
}
module.exports = {
CacheableDnsHttpAgent,
};

View file

@ -22,7 +22,10 @@ async function sendNotificationList(socket) {
]); ]);
for (let bean of list) { for (let bean of list) {
result.push(bean.export()); let notificationObject = bean.export();
notificationObject.isDefault = (notificationObject.isDefault === 1);
notificationObject.active = (notificationObject.active === 1);
result.push(notificationObject);
} }
io.to(socket.userID).emit("notificationList", result); io.to(socket.userID).emit("notificationList", result);

View file

@ -59,6 +59,7 @@ class Database {
"patch-monitor-expiry-notification.sql": true, "patch-monitor-expiry-notification.sql": true,
"patch-status-page-footer-css.sql": true, "patch-status-page-footer-css.sql": true,
"patch-added-mqtt-monitor.sql": true, "patch-added-mqtt-monitor.sql": true,
"patch-add-clickable-status-page-link.sql": true,
"patch-add-sqlserver-monitor.sql": true, "patch-add-sqlserver-monitor.sql": true,
"patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] }, "patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] },
}; };
@ -178,7 +179,13 @@ class Database {
} else { } else {
log.info("db", "Database patch is needed"); log.info("db", "Database patch is needed");
this.backup(version); try {
this.backup(version);
} catch (e) {
log.error("db", e);
log.error("db", "Unable to create a backup before patching the database. Please make sure you have enough space and permission.");
process.exit(1);
}
// Try catch anything here, if gone wrong, restore the backup // Try catch anything here, if gone wrong, restore the backup
try { try {
@ -446,6 +453,23 @@ class Database {
this.backupWalPath = walPath + ".bak" + version; this.backupWalPath = walPath + ".bak" + version;
fs.copyFileSync(walPath, this.backupWalPath); fs.copyFileSync(walPath, this.backupWalPath);
} }
// Double confirm if all files actually backup
if (!fs.existsSync(this.backupPath)) {
throw new Error("Backup failed! " + this.backupPath);
}
if (fs.existsSync(shmPath)) {
if (!fs.existsSync(this.backupShmPath)) {
throw new Error("Backup failed! " + this.backupShmPath);
}
}
if (fs.existsSync(walPath)) {
if (!fs.existsSync(this.backupWalPath)) {
throw new Error("Backup failed! " + this.backupWalPath);
}
}
} }
} }

View file

@ -31,7 +31,7 @@ class Group extends BeanModel {
*/ */
async getMonitorList() { async getMonitorList() {
return R.convertToBeans("monitor", await R.getAll(` return R.convertToBeans("monitor", await R.getAll(`
SELECT monitor.* FROM monitor, monitor_group SELECT monitor.*, monitor_group.send_url FROM monitor, monitor_group
WHERE monitor.id = monitor_group.monitor_id WHERE monitor.id = monitor_group.monitor_id
AND group_id = ? AND group_id = ?
ORDER BY monitor_group.weight ORDER BY monitor_group.weight

View file

@ -7,7 +7,7 @@ dayjs.extend(timezone);
const axios = require("axios"); const axios = require("axios");
const { Prometheus } = require("../prometheus"); const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, mqttAsync, setSetting, httpNtlm } = require("../util-server"); const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mqttAsync, setSetting, httpNtlm } = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model"); const { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification"); const { Notification } = require("../notification");
@ -16,12 +16,7 @@ const { demoMode } = require("../config");
const version = require("../../package.json").version; const version = require("../../package.json").version;
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const { CacheableDnsHttpAgent } = require("../cacheable-dns-http-agent");
const axiosCachedDnsResolve = require("esm-wallaby")(module)("axios-cached-dns-resolve");
// create an axios client instance with the cached DNS resolve interceptor
const axiosClient = axios.create();
axiosCachedDnsResolve.registerInterceptor(axiosClient);
/** /**
* status: * status:
@ -40,7 +35,13 @@ class Monitor extends BeanModel {
let obj = { let obj = {
id: this.id, id: this.id,
name: this.name, name: this.name,
sendUrl: this.sendUrl,
}; };
if (this.sendUrl) {
obj.url = this.url;
}
if (showTags) { if (showTags) {
obj.tags = await this.getTags(); obj.tags = await this.getTags();
} }
@ -290,7 +291,7 @@ class Monitor extends BeanModel {
}); });
} else { } else {
res = await axiosClient.request(options); res = await axios.request(options);
} }
bean.msg = `${res.status} - ${res.statusText}`; bean.msg = `${res.status} - ${res.statusText}`;
@ -437,16 +438,19 @@ class Monitor extends BeanModel {
throw new Error("Steam API Key not found"); throw new Error("Steam API Key not found");
} }
let res = await axiosClient.get(steamApiUrl, { let res = await axios.get(steamApiUrl, {
timeout: this.interval * 1000 * 0.8, timeout: this.interval * 1000 * 0.8,
headers: { headers: {
"Accept": "*/*", "Accept": "*/*",
"User-Agent": "Uptime-Kuma/" + version, "User-Agent": "Uptime-Kuma/" + version,
}, },
httpsAgent: new https.Agent({ httpsAgent: CacheableDnsHttpAgent.getHttpsAgent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: !this.getIgnoreTls(), rejectUnauthorized: !this.getIgnoreTls(),
}), }),
httpAgent: CacheableDnsHttpAgent.getHttpAgent({
maxCachedSessions: 0,
}),
maxRedirects: this.maxredirects, maxRedirects: this.maxredirects,
validateStatus: (status) => { validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes()); return checkStatusCode(status, this.getAcceptedStatuscodes());
@ -509,6 +513,14 @@ class Monitor extends BeanModel {
await mssqlQuery(this.databaseConnectionString, this.databaseQuery); await mssqlQuery(this.databaseConnectionString, this.databaseQuery);
bean.msg = "";
bean.status = UP;
bean.ping = dayjs().valueOf() - startTime;
} else if (this.type === "postgres") {
let startTime = dayjs().valueOf();
await postgresQuery(this.databaseConnectionString, this.databaseQuery);
bean.msg = ""; bean.msg = "";
bean.status = UP; bean.status = UP;
bean.ping = dayjs().valueOf() - startTime; bean.ping = dayjs().valueOf() - startTime;

View file

@ -45,6 +45,8 @@ class StatusPage extends BeanModel {
$("link[rel=icon]") $("link[rel=icon]")
.attr("href", statusPage.icon) .attr("href", statusPage.icon)
.removeAttr("type"); .removeAttr("type");
$("link[rel=apple-touch-icon]").remove();
} }
const head = $("head"); const head = $("head");
@ -61,6 +63,9 @@ class StatusPage extends BeanModel {
</script> </script>
`); `);
// manifest.json
$("link[rel=manifest]").attr("href", `/api/status-page/${statusPage.slug}/manifest.json`);
return $.root().html(); return $.root().html();
} }

View file

@ -0,0 +1,50 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { setting } = require("../util-server");
const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util");
class AlertNow extends NotificationProvider {
name = "AlertNow";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let textMsg = "";
let status = "open";
let eventType = "ERROR";
let eventId = new Date().toISOString().slice(0, 10).replace(/-/g, "");
if (heartbeatJSON && heartbeatJSON.status === UP) {
textMsg = `[${heartbeatJSON.name}] ✅ Application is back online`;
status = "close";
eventType = "INFO";
eventId += `_${heartbeatJSON.name.replace(/\s/g, "")}`;
} else if (heartbeatJSON && heartbeatJSON.status === DOWN) {
textMsg = `[${heartbeatJSON.name}] 🔴 Application went down`;
}
textMsg += ` - ${msg}`;
const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorJSON) {
textMsg += ` >> ${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
}
const data = {
"summary": textMsg,
"status": status,
"event_type": eventType,
"event_id": eventId,
};
await axios.post(notification.alertNowWebhookURL, data);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = AlertNow;

View file

@ -14,7 +14,7 @@ class LunaSea extends NotificationProvider {
if (heartbeatJSON == null) { if (heartbeatJSON == null) {
let testdata = { let testdata = {
"title": "Uptime Kuma Alert", "title": "Uptime Kuma Alert",
"body": "Testing Successful.", "body": msg,
}; };
await axios.post(lunaseadevice, testdata); await axios.post(lunaseadevice, testdata);
return okMsg; return okMsg;

View file

@ -1,40 +1,41 @@
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { log } = require("../src/util");
const Alerta = require("./notification-providers/alerta");
const AlertNow = require("./notification-providers/alertnow");
const AliyunSms = require("./notification-providers/aliyun-sms");
const Apprise = require("./notification-providers/apprise"); const Apprise = require("./notification-providers/apprise");
const Bark = require("./notification-providers/bark");
const ClickSendSMS = require("./notification-providers/clicksendsms");
const DingDing = require("./notification-providers/dingding");
const Discord = require("./notification-providers/discord"); const Discord = require("./notification-providers/discord");
const Feishu = require("./notification-providers/feishu");
const GoogleChat = require("./notification-providers/google-chat");
const Gorush = require("./notification-providers/gorush");
const Gotify = require("./notification-providers/gotify"); const Gotify = require("./notification-providers/gotify");
const Ntfy = require("./notification-providers/ntfy");
const Line = require("./notification-providers/line"); const Line = require("./notification-providers/line");
const LunaSea = require("./notification-providers/lunasea"); const LunaSea = require("./notification-providers/lunasea");
const Mattermost = require("./notification-providers/mattermost");
const Matrix = require("./notification-providers/matrix"); const Matrix = require("./notification-providers/matrix");
const Mattermost = require("./notification-providers/mattermost");
const Ntfy = require("./notification-providers/ntfy");
const Octopush = require("./notification-providers/octopush"); const Octopush = require("./notification-providers/octopush");
const OneBot = require("./notification-providers/onebot");
const PagerDuty = require("./notification-providers/pagerduty");
const PromoSMS = require("./notification-providers/promosms"); const PromoSMS = require("./notification-providers/promosms");
const ClickSendSMS = require("./notification-providers/clicksendsms");
const Pushbullet = require("./notification-providers/pushbullet"); const Pushbullet = require("./notification-providers/pushbullet");
const PushDeer = require("./notification-providers/pushdeer");
const Pushover = require("./notification-providers/pushover"); const Pushover = require("./notification-providers/pushover");
const Pushy = require("./notification-providers/pushy"); const Pushy = require("./notification-providers/pushy");
const TechulusPush = require("./notification-providers/techulus-push");
const RocketChat = require("./notification-providers/rocket-chat"); const RocketChat = require("./notification-providers/rocket-chat");
const SerwerSMS = require("./notification-providers/serwersms");
const Signal = require("./notification-providers/signal"); const Signal = require("./notification-providers/signal");
const Slack = require("./notification-providers/slack"); const Slack = require("./notification-providers/slack");
const SMTP = require("./notification-providers/smtp"); const SMTP = require("./notification-providers/smtp");
const Stackfield = require("./notification-providers/stackfield");
const Teams = require("./notification-providers/teams"); const Teams = require("./notification-providers/teams");
const TechulusPush = require("./notification-providers/techulus-push");
const Telegram = require("./notification-providers/telegram"); const Telegram = require("./notification-providers/telegram");
const Webhook = require("./notification-providers/webhook"); const Webhook = require("./notification-providers/webhook");
const Feishu = require("./notification-providers/feishu");
const AliyunSms = require("./notification-providers/aliyun-sms");
const DingDing = require("./notification-providers/dingding");
const Bark = require("./notification-providers/bark");
const { log } = require("../src/util");
const SerwerSMS = require("./notification-providers/serwersms");
const Stackfield = require("./notification-providers/stackfield");
const WeCom = require("./notification-providers/wecom"); const WeCom = require("./notification-providers/wecom");
const GoogleChat = require("./notification-providers/google-chat");
const PagerDuty = require("./notification-providers/pagerduty");
const Gorush = require("./notification-providers/gorush");
const Alerta = require("./notification-providers/alerta");
const OneBot = require("./notification-providers/onebot");
const PushDeer = require("./notification-providers/pushdeer");
class Notification { class Notification {
@ -47,41 +48,42 @@ class Notification {
this.providerList = {}; this.providerList = {};
const list = [ const list = [
new Apprise(), new Alerta(),
new AlertNow(),
new AliyunSms(), new AliyunSms(),
new Apprise(),
new Bark(),
new ClickSendSMS(),
new DingDing(), new DingDing(),
new Discord(), new Discord(),
new Teams(), new Feishu(),
new GoogleChat(),
new Gorush(),
new Gotify(), new Gotify(),
new Ntfy(),
new Line(), new Line(),
new LunaSea(), new LunaSea(),
new Feishu(),
new Mattermost(),
new Matrix(), new Matrix(),
new Mattermost(),
new Ntfy(),
new Octopush(), new Octopush(),
new OneBot(),
new PagerDuty(),
new PromoSMS(), new PromoSMS(),
new ClickSendSMS(),
new Pushbullet(), new Pushbullet(),
new PushDeer(),
new Pushover(), new Pushover(),
new Pushy(), new Pushy(),
new TechulusPush(),
new RocketChat(), new RocketChat(),
new SerwerSMS(),
new Signal(), new Signal(),
new Slack(), new Slack(),
new SMTP(), new SMTP(),
new Stackfield(),
new Teams(),
new TechulusPush(),
new Telegram(), new Telegram(),
new Webhook(), new Webhook(),
new Bark(),
new SerwerSMS(),
new Stackfield(),
new WeCom(), new WeCom(),
new GoogleChat(),
new PagerDuty(),
new Gorush(),
new Alerta(),
new OneBot(),
new PushDeer(),
]; ];
for (let item of list) { for (let item of list) {

View file

@ -107,4 +107,42 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
} }
}); });
// Status page's manifest.json
router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async (request, response) => {
allowDevAllOrigin(response);
let slug = request.params.slug;
try {
// Get Status Page
let statusPage = await R.findOne("status_page", " slug = ? ", [
slug
]);
if (!statusPage) {
response.statusCode = 404;
response.json({
msg: "Not Found"
});
return;
}
// Response
response.json({
"name": statusPage.title,
"start_url": "/status/" + statusPage.slug,
"display": "standalone",
"icons": [
{
"src": statusPage.icon,
"sizes": "128x128",
"type": "image/png"
}
]
});
} catch (error) {
send403(response, error.message);
}
});
module.exports = router; module.exports = router;

View file

@ -655,7 +655,7 @@ let needSetup = false;
bean.retryInterval = monitor.retryInterval; bean.retryInterval = monitor.retryInterval;
bean.hostname = monitor.hostname; bean.hostname = monitor.hostname;
bean.maxretries = monitor.maxretries; bean.maxretries = monitor.maxretries;
bean.port = monitor.port; bean.port = parseInt(monitor.port);
bean.keyword = monitor.keyword; bean.keyword = monitor.keyword;
bean.ignoreTls = monitor.ignoreTls; bean.ignoreTls = monitor.ignoreTls;
bean.expiryNotification = monitor.expiryNotification; bean.expiryNotification = monitor.expiryNotification;

165
server/settings.js Normal file
View file

@ -0,0 +1,165 @@
const { R } = require("redbean-node");
const { log } = require("../src/util");
class Settings {
/**
* Example:
* {
* key1: {
* value: "value2",
* timestamp: 12345678
* },
* key2: {
* value: 2,
* timestamp: 12345678
* },
* }
* @type {{}}
*/
static cacheList = {
};
static cacheCleaner = null;
/**
* Retrieve value of setting based on key
* @param {string} key Key of setting to retrieve
* @returns {Promise<any>} Value
*/
static async get(key) {
// Start cache clear if not started yet
if (!Settings.cacheCleaner) {
Settings.cacheCleaner = setInterval(() => {
log.debug("settings", "Cache Cleaner is just started.");
for (key in Settings.cacheList) {
if (Date.now() - Settings.cacheList[key].timestamp > 60 * 1000) {
log.debug("settings", "Cache Cleaner deleted: " + key);
delete Settings.cacheList[key];
}
}
}, 60 * 1000);
}
// Query from cache
if (key in Settings.cacheList) {
const v = Settings.cacheList[key].value;
log.debug("settings", `Get Setting (cache): ${key}: ${v}`);
return v;
}
let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [
key,
]);
try {
const v = JSON.parse(value);
log.debug("settings", `Get Setting: ${key}: ${v}`);
Settings.cacheList[key] = {
value: v,
timestamp: Date.now()
};
return v;
} catch (e) {
return value;
}
}
/**
* Sets the specified setting to specified value
* @param {string} key Key of setting to set
* @param {any} value Value to set to
* @param {?string} type Type of setting
* @returns {Promise<void>}
*/
static async set(key, value, type = null) {
let bean = await R.findOne("setting", " `key` = ? ", [
key,
]);
if (!bean) {
bean = R.dispense("setting");
bean.key = key;
}
bean.type = type;
bean.value = JSON.stringify(value);
await R.store(bean);
Settings.deleteCache([ key ]);
}
/**
* Get settings based on type
* @param {string} type The type of setting
* @returns {Promise<Bean>}
*/
static async getSettings(type) {
let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [
type,
]);
let result = {};
for (let row of list) {
try {
result[row.key] = JSON.parse(row.value);
} catch (e) {
result[row.key] = row.value;
}
}
return result;
}
/**
* Set settings based on type
* @param {string} type Type of settings to set
* @param {Object} data Values of settings
* @returns {Promise<void>}
*/
static async setSettings(type, data) {
let keyList = Object.keys(data);
let promiseList = [];
for (let key of keyList) {
let bean = await R.findOne("setting", " `key` = ? ", [
key
]);
if (bean == null) {
bean = R.dispense("setting");
bean.type = type;
bean.key = key;
}
if (bean.type === type) {
bean.value = JSON.stringify(data[key]);
promiseList.push(R.store(bean));
}
}
await Promise.all(promiseList);
Settings.deleteCache(keyList);
}
/**
*
* @param {string[]} keyList
*/
static deleteCache(keyList) {
for (let key of keyList) {
delete Settings.cacheList[key];
}
}
}
module.exports = {
Settings,
};

View file

@ -63,7 +63,10 @@ module.exports.cloudflaredSocketHandler = (socket) => {
socket.on(prefix + "stop", async (currentPassword, callback) => { socket.on(prefix + "stop", async (currentPassword, callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
await doubleCheckPassword(socket, currentPassword); const disabledAuth = await setting("disableAuth");
if (!disabledAuth) {
await doubleCheckPassword(socket, currentPassword);
}
cloudflared.stop(); cloudflared.stop();
} catch (error) { } catch (error) {
callback({ callback({

View file

@ -202,6 +202,11 @@ module.exports.statusPageSocketHandler = (socket) => {
relationBean.weight = monitorOrder++; relationBean.weight = monitorOrder++;
relationBean.group_id = groupBean.id; relationBean.group_id = groupBean.id;
relationBean.monitor_id = monitor.id; relationBean.monitor_id = monitor.id;
if (monitor.sendUrl !== undefined) {
relationBean.send_url = monitor.sendUrl;
}
await R.store(relationBean); await R.store(relationBean);
} }

View file

@ -7,6 +7,7 @@ const { R } = require("redbean-node");
const { log } = require("../src/util"); const { log } = require("../src/util");
const Database = require("./database"); const Database = require("./database");
const util = require("util"); const util = require("util");
const { CacheableDnsHttpAgent } = require("./cacheable-dns-http-agent");
/** /**
* `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue. * `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue.
@ -71,6 +72,8 @@ class UptimeKumaServer {
} }
} }
CacheableDnsHttpAgent.registerGlobalAgent();
this.io = new Server(this.httpServer); this.io = new Server(this.httpServer);
} }

View file

@ -11,7 +11,10 @@ const mqtt = require("mqtt");
const chroma = require("chroma-js"); const chroma = require("chroma-js");
const { badgeConstants } = require("./config"); const { badgeConstants } = require("./config");
const mssql = require("mssql"); const mssql = require("mssql");
const { Client } = require("pg");
const postgresConParse = require("pg-connection-string").parse;
const { NtlmClient } = require("axios-ntlm"); const { NtlmClient } = require("axios-ntlm");
const { Settings } = require("./settings");
// From ping-lite // From ping-lite
exports.WIN = /^win/.test(process.platform); exports.WIN = /^win/.test(process.platform);
@ -237,10 +240,6 @@ exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype) {
*/ */
exports.mssqlQuery = function (connectionString, query) { exports.mssqlQuery = function (connectionString, query) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
mssql.on("error", err => {
reject(err);
});
mssql.connect(connectionString).then(pool => { mssql.connect(connectionString).then(pool => {
return pool.request() return pool.request()
.query(query); .query(query);
@ -254,23 +253,45 @@ exports.mssqlQuery = function (connectionString, query) {
}); });
}; };
/**
* Run a query on Postgres
* @param {string} connectionString The database connection string
* @param {string} query The query to validate the database with
* @returns {Promise<(string[]|Object[]|Object)>}
*/
exports.postgresQuery = function (connectionString, query) {
return new Promise((resolve, reject) => {
const config = postgresConParse(connectionString);
if (config.password === "") {
// See https://github.com/brianc/node-postgres/issues/1927
return reject(new Error("Password is undefined."));
}
const client = new Client({ connectionString });
client.connect();
return client.query(query)
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
})
.finally(() => {
client.end();
});
});
};
/** /**
* Retrieve value of setting based on key * Retrieve value of setting based on key
* @param {string} key Key of setting to retrieve * @param {string} key Key of setting to retrieve
* @returns {Promise<any>} Value * @returns {Promise<any>} Value
*/ */
exports.setting = async function (key) { exports.setting = async function (key) {
let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [ return await Settings.get(key);
key,
]);
try {
const v = JSON.parse(value);
log.debug("util", `Get Setting: ${key}: ${v}`);
return v;
} catch (e) {
return value;
}
}; };
/** /**
@ -281,70 +302,26 @@ exports.setting = async function (key) {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
exports.setSetting = async function (key, value, type = null) { exports.setSetting = async function (key, value, type = null) {
let bean = await R.findOne("setting", " `key` = ? ", [ await Settings.set(key, value, type);
key,
]);
if (!bean) {
bean = R.dispense("setting");
bean.key = key;
}
bean.type = type;
bean.value = JSON.stringify(value);
await R.store(bean);
}; };
/** /**
* Get settings based on type * Get settings based on type
* @param {?string} type The type of setting * @param {string} type The type of setting
* @returns {Promise<Bean>} * @returns {Promise<Bean>}
*/ */
exports.getSettings = async function (type) { exports.getSettings = async function (type) {
let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [ return await Settings.getSettings(type);
type,
]);
let result = {};
for (let row of list) {
try {
result[row.key] = JSON.parse(row.value);
} catch (e) {
result[row.key] = row.value;
}
}
return result;
}; };
/** /**
* Set settings based on type * Set settings based on type
* @param {?string} type Type of settings to set * @param {string} type Type of settings to set
* @param {Object} data Values of settings * @param {Object} data Values of settings
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
exports.setSettings = async function (type, data) { exports.setSettings = async function (type, data) {
let keyList = Object.keys(data); await Settings.setSettings(type, data);
let promiseList = [];
for (let key of keyList) {
let bean = await R.findOne("setting", " `key` = ? ", [
key
]);
if (bean == null) {
bean = R.dispense("setting");
bean.type = type;
bean.key = key;
}
if (bean.type === type) {
bean.value = JSON.stringify(data[key]);
promiseList.push(R.store(bean));
}
}
await Promise.all(promiseList);
}; };
// ssl-checker by @dyaa // ssl-checker by @dyaa
@ -437,7 +414,7 @@ exports.checkCertificate = function (res) {
/** /**
* Check if the provided status code is within the accepted ranges * Check if the provided status code is within the accepted ranges
* @param {string} status The status code to check * @param {number} status The status code to check
* @param {string[]} acceptedCodes An array of accepted status codes * @param {string[]} acceptedCodes An array of accepted status codes
* @returns {boolean} True if status code within range, false otherwise * @returns {boolean} True if status code within range, false otherwise
* @throws {Error} Will throw an error if the provided status code is not a valid range string or code string * @throws {Error} Will throw an error if the provided status code is not a valid range string or code string

View file

@ -25,10 +25,12 @@ export default {
CertificateInfoRow, CertificateInfoRow,
}, },
props: { props: {
/** Object representing certificate */
certInfo: { certInfo: {
type: Object, type: Object,
required: true, required: true,
}, },
/** Is the TLS certificate valid? */
valid: { valid: {
type: Boolean, type: Boolean,
required: true, required: true,

View file

@ -56,12 +56,19 @@ export default {
Datetime, Datetime,
}, },
props: { props: {
/** Object representing certificate */
cert: { cert: {
type: Object, type: Object,
required: true, required: true,
}, },
}, },
methods: { methods: {
/**
* Format the subject of the certificate
* @param {Object} subject Object representing the certificates
* subject
* @returns {string}
*/
formatSubject(subject) { formatSubject(subject) {
if (subject.O && subject.CN && subject.C) { if (subject.O && subject.CN && subject.C) {
return `${subject.CN} - ${subject.O} (${subject.C})`; return `${subject.CN} - ${subject.O} (${subject.C})`;

View file

@ -29,14 +29,17 @@ import { Modal } from "bootstrap";
export default { export default {
props: { props: {
/** Style of button */
btnStyle: { btnStyle: {
type: String, type: String,
default: "btn-primary", default: "btn-primary",
}, },
/** Text to use as yes */
yesText: { yesText: {
type: String, type: String,
default: "Yes", // TODO: No idea what to translate this default: "Yes", // TODO: No idea what to translate this
}, },
/** Text to use as no */
noText: { noText: {
type: String, type: String,
default: "No", default: "No",
@ -50,9 +53,13 @@ export default {
this.modal = new Modal(this.$refs.modal); this.modal = new Modal(this.$refs.modal);
}, },
methods: { methods: {
/** Show the confirm dialog */
show() { show() {
this.modal.show(); this.modal.show();
}, },
/**
* @emits string "yes" Notify the parent when Yes is pressed
*/
yes() { yes() {
this.$emit("yes"); this.$emit("yes");
}, },

View file

@ -25,33 +25,41 @@ let timeout;
export default { export default {
props: { props: {
/** ID of this input */
id: { id: {
type: String, type: String,
default: "" default: ""
}, },
/** Type of input */
type: { type: {
type: String, type: String,
default: "text" default: "text"
}, },
/** The value of the input */
modelValue: { modelValue: {
type: String, type: String,
default: "" default: ""
}, },
/** A placeholder to use */
placeholder: { placeholder: {
type: String, type: String,
default: "" default: ""
}, },
/** Should the field auto complete */
autocomplete: { autocomplete: {
type: String, type: String,
default: undefined, default: undefined,
}, },
/** Is the input required? */
required: { required: {
type: Boolean type: Boolean
}, },
/** Should the input be read only? */
readonly: { readonly: {
type: String, type: String,
default: undefined, default: undefined,
}, },
/** Is the input disabled? */
disabled: { disabled: {
type: String, type: String,
default: undefined, default: undefined,
@ -79,14 +87,21 @@ export default {
}, },
methods: { methods: {
/** Show the input */
showInput() { showInput() {
this.visibility = "text"; this.visibility = "text";
}, },
/** Hide the input */
hideInput() { hideInput() {
this.visibility = "password"; this.visibility = "password";
}, },
/**
* Copy the provided text to the users clipboard
* @param {string} textToCopy
* @returns {Promise<void>}
*/
copyToClipboard(textToCopy) { copyToClipboard(textToCopy) {
this.icon = "check"; this.icon = "check";

View file

@ -10,6 +10,7 @@ import { sleep } from "../util.ts";
export default { export default {
props: { props: {
/** Value to count */
value: { value: {
type: [ String, Number ], type: [ String, Number ],
default: 0, default: 0,
@ -18,6 +19,7 @@ export default {
type: Number, type: Number,
default: 0.3, default: 0.3,
}, },
/** Unit of the value */
unit: { unit: {
type: String, type: String,
default: "ms", default: "ms",
@ -43,9 +45,7 @@ export default {
let frames = 12; let frames = 12;
let step = Math.floor(diff / frames); let step = Math.floor(diff / frames);
if (isNaN(step) || ! this.isNum || (diff > 0 && step < 1) || (diff < 0 && step > 1) || diff === 0) { if (! (isNaN(step) || ! this.isNum || (diff > 0 && step < 1) || (diff < 0 && step > 1) || diff === 0)) {
// Lazy to NOT this condition, hahaha.
} else {
for (let i = 1; i < frames; i++) { for (let i = 1; i < frames; i++) {
this.output += step; this.output += step;
await sleep(15); await sleep(15);

View file

@ -13,10 +13,12 @@ dayjs.extend(relativeTime);
export default { export default {
props: { props: {
/** Value of date time */
value: { value: {
type: String, type: String,
default: null, default: null,
}, },
/** Should only the date be displayed? */
dateOnly: { dateOnly: {
type: Boolean, type: Boolean,
default: false, default: false,

View file

@ -17,14 +17,17 @@
export default { export default {
props: { props: {
/** Size of the heartbeat bar */
size: { size: {
type: String, type: String,
default: "big", default: "big",
}, },
/** ID of the monitor */
monitorId: { monitorId: {
type: Number, type: Number,
required: true, required: true,
}, },
/** Array of the monitors heartbeats */
heartbeatList: { heartbeatList: {
type: Array, type: Array,
default: null, default: null,
@ -160,12 +163,19 @@ export default {
this.resize(); this.resize();
}, },
methods: { methods: {
/** Resize the heartbeat bar */
resize() { resize() {
if (this.$refs.wrap) { if (this.$refs.wrap) {
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)); this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2));
} }
}, },
/**
* Get the title of the beat.
* Used as the hover tooltip on the heartbeat bar.
* @param {Object} beat Beat to get title from
* @returns {string}
*/
getBeatTitle(beat) { getBeatTitle(beat) {
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ""); return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
}, },

View file

@ -24,25 +24,31 @@
<script> <script>
export default { export default {
props: { props: {
/** The value of the input */
modelValue: { modelValue: {
type: String, type: String,
default: "" default: ""
}, },
/** A placeholder to use */
placeholder: { placeholder: {
type: String, type: String,
default: "" default: ""
}, },
/** Maximum length of the input */
maxlength: { maxlength: {
type: Number, type: Number,
default: 255 default: 255
}, },
/** Should the field auto complete */
autocomplete: { autocomplete: {
type: String, type: String,
default: undefined, default: undefined,
}, },
/** Is the input required? */
required: { required: {
type: Boolean type: Boolean
}, },
/** Should the input be read only? */
readonly: { readonly: {
type: String, type: String,
default: undefined, default: undefined,
@ -68,9 +74,11 @@ export default {
}, },
methods: { methods: {
/** Show users input in plain text */
showInput() { showInput() {
this.visibility = "text"; this.visibility = "text";
}, },
/** Censor users input */
hideInput() { hideInput() {
this.visibility = "password"; this.visibility = "password";
}, },

View file

@ -55,6 +55,7 @@ export default {
}; };
}, },
methods: { methods: {
/** Submit the user details and attempt to log in */
submit() { submit() {
this.processing = true; this.processing = true;

View file

@ -58,6 +58,7 @@ export default {
Tag, Tag,
}, },
props: { props: {
/** Should the scrollbar be shown */
scrollbar: { scrollbar: {
type: Boolean, type: Boolean,
}, },
@ -136,6 +137,7 @@ export default {
window.removeEventListener("scroll", this.onScroll); window.removeEventListener("scroll", this.onScroll);
}, },
methods: { methods: {
/** Handle user scroll */
onScroll() { onScroll() {
if (window.top.scrollY <= 133) { if (window.top.scrollY <= 133) {
this.windowTop = window.top.scrollY; this.windowTop = window.top.scrollY;
@ -143,9 +145,15 @@ export default {
this.windowTop = 133; this.windowTop = 133;
} }
}, },
/**
* Get URL of monitor
* @param {number} id ID of monitor
* @returns {string} Relative URL of monitor
*/
monitorURL(id) { monitorURL(id) {
return getMonitorRelativeURL(id); return getMonitorRelativeURL(id);
}, },
/** Clear the search bar */
clearSearchText() { clearSearchText() {
this.searchText = ""; this.searchText = "";
} }

View file

@ -125,11 +125,16 @@ export default {
}, },
methods: { methods: {
/** Show dialog to confirm deletion */
deleteConfirm() { deleteConfirm() {
this.modal.hide(); this.modal.hide();
this.$refs.confirmDelete.show(); this.$refs.confirmDelete.show();
}, },
/**
* Show settings for specified notification
* @param {number} notificationID ID of notification to show
*/
show(notificationID) { show(notificationID) {
if (notificationID) { if (notificationID) {
this.id = notificationID; this.id = notificationID;
@ -152,6 +157,7 @@ export default {
this.modal.show(); this.modal.show();
}, },
/** Submit the form to the server */
submit() { submit() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => { this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => {
@ -170,6 +176,7 @@ export default {
}); });
}, },
/** Test the notification endpoint */
test() { test() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("testNotification", this.notification, (res) => { this.$root.getSocket().emit("testNotification", this.notification, (res) => {
@ -178,6 +185,7 @@ export default {
}); });
}, },
/** Delete the notification endpoint */
deleteNotification() { deleteNotification() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("deleteNotification", this.id, (res) => { this.$root.getSocket().emit("deleteNotification", this.id, (res) => {
@ -190,6 +198,7 @@ export default {
}); });
}, },
/** /**
* Get a unique default name for the notification
* @param {keyof NotificationFormList} notificationKey * @param {keyof NotificationFormList} notificationKey
* @return {string} * @return {string}
*/ */

View file

@ -35,6 +35,7 @@ Chart.register(LineController, BarController, LineElement, PointElement, TimeSca
export default { export default {
components: { LineChart }, components: { LineChart },
props: { props: {
/** ID of monitor */
monitorId: { monitorId: {
type: Number, type: Number,
required: true, required: true,

View file

@ -130,11 +130,16 @@ export default {
}, },
methods: { methods: {
/** Show dialog to confirm deletion */
deleteConfirm() { deleteConfirm() {
this.modal.hide(); this.modal.hide();
this.$refs.confirmDelete.show(); this.$refs.confirmDelete.show();
}, },
/**
* Show settings for specified proxy
* @param {number} proxyID ID of proxy to show
*/
show(proxyID) { show(proxyID) {
if (proxyID) { if (proxyID) {
this.id = proxyID; this.id = proxyID;
@ -163,6 +168,7 @@ export default {
this.modal.show(); this.modal.show();
}, },
/** Submit form data for saving */
submit() { submit() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("addProxy", this.proxy, this.id, (res) => { this.$root.getSocket().emit("addProxy", this.proxy, this.id, (res) => {
@ -180,6 +186,7 @@ export default {
}); });
}, },
/** Delete this proxy */
deleteProxy() { deleteProxy() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("deleteProxy", this.id, (res) => { this.$root.getSocket().emit("deleteProxy", this.id, (res) => {

View file

@ -39,7 +39,27 @@
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" /> <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
<Uptime :monitor="monitor.element" type="24" :pill="true" /> <Uptime :monitor="monitor.element" type="24" :pill="true" />
{{ monitor.element.name }} <a
v-if="showLink(monitor)"
:href="monitor.element.url"
class="item-name"
target="_blank"
>
{{ monitor.element.name }}
</a>
<p v-else class="item-name"> {{ monitor.element.name }} </p>
<span
v-if="showLink(monitor, true)"
title="Toggle Clickable Link"
>
<font-awesome-icon
v-if="editMode"
:class="{'link-active': monitor.element.sendUrl, 'btn-link': true}"
icon="link" class="action me-3"
@click="toggleLink(group.index, monitor.index)"
/>
</span>
</div> </div>
<div v-if="showTags" class="tags"> <div v-if="showTags" class="tags">
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
@ -72,10 +92,12 @@ export default {
Tag, Tag,
}, },
props: { props: {
/** Are we in edit mode? */
editMode: { editMode: {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
/** Should tags be shown? */
showTags: { showTags: {
type: Boolean, type: Boolean,
} }
@ -94,13 +116,50 @@ export default {
}, },
methods: { methods: {
/**
* Remove the specified group
* @param {number} index Index of group to remove
*/
removeGroup(index) { removeGroup(index) {
this.$root.publicGroupList.splice(index, 1); this.$root.publicGroupList.splice(index, 1);
}, },
/**
* Remove a monitor from a group
* @param {number} groupIndex Index of group to remove monitor
* from
* @param {number} index Index of monitor to remove
*/
removeMonitor(groupIndex, index) { removeMonitor(groupIndex, index) {
this.$root.publicGroupList[groupIndex].monitorList.splice(index, 1); this.$root.publicGroupList[groupIndex].monitorList.splice(index, 1);
}, },
/**
* Toggle the value of sendUrl
* @param {number} groupIndex Index of group monitor is member of
* @param {number} index Index of monitor within group
*/
toggleLink(groupIndex, index) {
this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl = !this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl;
},
/**
* Should a link to the monitor be shown?
* Attempts to guess if a link should be shown based upon if
* sendUrl is set and if the URL is default or not.
* @param {Object} monitor Monitor to check
* @param {boolean} [ignoreSendUrl=false] Should the presence of the sendUrl
* property be ignored. This will only work in edit mode.
* @returns {boolean}
*/
showLink(monitor, ignoreSendUrl = false) {
// We must check if there are any elements in monitorList to
// prevent undefined errors if it hasn't been loaded yet
if (this.$parent.editMode && ignoreSendUrl && Object.keys(this.$root.monitorList).length) {
return this.$root.monitorList[monitor.element.id].type === "http" || this.$root.monitorList[monitor.element.id].type === "keyword";
}
return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode;
},
} }
}; };
</script> </script>
@ -119,6 +178,22 @@ export default {
min-height: 46px; min-height: 46px;
} }
.item-name {
padding-left: 5px;
padding-right: 5px;
margin: 0;
display: inline-block;
}
.btn-link {
color: #bbbbbb;
margin-left: 5px;
}
.link-active {
color: $primary;
}
.flip-list-move { .flip-list-move {
transition: transform 0.5s; transition: transform 0.5s;
} }

View file

@ -5,6 +5,7 @@
<script> <script>
export default { export default {
props: { props: {
/** Current status of monitor */
status: { status: {
type: Number, type: Number,
default: 0, default: 0,

View file

@ -20,14 +20,20 @@
<script> <script>
export default { export default {
props: { props: {
/** Object representing tag */
item: { item: {
type: Object, type: Object,
required: true, required: true,
}, },
/** Function to remove tag */
remove: { remove: {
type: Function, type: Function,
default: null, default: null,
}, },
/**
* Size of tag
* @values normal, small
*/
size: { size: {
type: String, type: String,
default: "normal", default: "normal",

View file

@ -139,6 +139,7 @@ export default {
VueMultiselect, VueMultiselect,
}, },
props: { props: {
/** Array of tags to be pre-selected */
preSelectedTags: { preSelectedTags: {
type: Array, type: Array,
default: () => [], default: () => [],
@ -241,9 +242,11 @@ export default {
this.getExistingTags(); this.getExistingTags();
}, },
methods: { methods: {
/** Show the add tag dialog */
showAddDialog() { showAddDialog() {
this.modal.show(); this.modal.show();
}, },
/** Get all existing tags */
getExistingTags() { getExistingTags() {
this.$root.getSocket().emit("getTags", (res) => { this.$root.getSocket().emit("getTags", (res) => {
if (res.ok) { if (res.ok) {
@ -253,6 +256,10 @@ export default {
} }
}); });
}, },
/**
* Delete the specified tag
* @param {Object} tag Object representing tag to delete
*/
deleteTag(item) { deleteTag(item) {
if (item.new) { if (item.new) {
// Undo Adding a new Tag // Undo Adding a new Tag
@ -262,6 +269,13 @@ export default {
this.deleteTags.push(item); this.deleteTags.push(item);
} }
}, },
/**
* Get colour of text inside the tag
* @param {Object} option The tag that needs to be displayed.
* Defaults to "white" unless the tag has no color, which will
* then return the body color (based on application theme)
* @returns string
*/
textColor(option) { textColor(option) {
if (option.color) { if (option.color) {
return "white"; return "white";
@ -269,6 +283,7 @@ export default {
return this.$root.theme === "light" ? "var(--bs-body-color)" : "inherit"; return this.$root.theme === "light" ? "var(--bs-body-color)" : "inherit";
} }
}, },
/** Add a draft tag */
addDraftTag() { addDraftTag() {
console.log("Adding Draft Tag: ", this.newDraftTag); console.log("Adding Draft Tag: ", this.newDraftTag);
if (this.newDraftTag.select != null) { if (this.newDraftTag.select != null) {
@ -296,6 +311,7 @@ export default {
} }
this.clearDraftTag(); this.clearDraftTag();
}, },
/** Remove a draft tag */
clearDraftTag() { clearDraftTag() {
this.newDraftTag = { this.newDraftTag = {
name: null, name: null,
@ -307,26 +323,51 @@ export default {
}; };
this.modal.hide(); this.modal.hide();
}, },
/**
* Add a tag asynchronously
* @param {Object} newTag Object representing new tag to add
* @returns {Promise<void>}
*/
addTagAsync(newTag) { addTagAsync(newTag) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.$root.getSocket().emit("addTag", newTag, resolve); this.$root.getSocket().emit("addTag", newTag, resolve);
}); });
}, },
/**
* Add a tag to a monitor asynchronously
* @param {number} tagId ID of tag to add
* @param {number} monitorId ID of monitor to add tag to
* @param {string} value Value of tag
* @returns {Promise<void>}
*/
addMonitorTagAsync(tagId, monitorId, value) { addMonitorTagAsync(tagId, monitorId, value) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.$root.getSocket().emit("addMonitorTag", tagId, monitorId, value, resolve); this.$root.getSocket().emit("addMonitorTag", tagId, monitorId, value, resolve);
}); });
}, },
/**
* Delete a tag from a monitor asynchronously
* @param {number} tagId ID of tag to remove
* @param {number} monitorId ID of monitor to remove tag from
* @param {string} value Value of tag
* @returns {Promise<void>}
*/
deleteMonitorTagAsync(tagId, monitorId, value) { deleteMonitorTagAsync(tagId, monitorId, value) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.$root.getSocket().emit("deleteMonitorTag", tagId, monitorId, value, resolve); this.$root.getSocket().emit("deleteMonitorTag", tagId, monitorId, value, resolve);
}); });
}, },
/** Handle pressing Enter key when inside the modal */
onEnter() { onEnter() {
if (!this.validateDraftTag.invalid) { if (!this.validateDraftTag.invalid) {
this.addDraftTag(); this.addDraftTag();
} }
}, },
/**
* Submit the form data
* @param {number} monitorId ID of monitor this change affects
* @returns {void}
*/
async submit(monitorId) { async submit(monitorId) {
console.log(`Submitting tag changes for monitor ${monitorId}...`); console.log(`Submitting tag changes for monitor ${monitorId}...`);
this.processing = true; this.processing = true;

View file

@ -29,10 +29,12 @@
<script> <script>
export default { export default {
props: { props: {
/** Heading of the section */
heading: { heading: {
type: String, type: String,
default: "", default: "",
}, },
/** Should the section be open by default? */
defaultOpen: { defaultOpen: {
type: Boolean, type: Boolean,
default: false, default: false,

View file

@ -100,18 +100,22 @@ export default {
this.getStatus(); this.getStatus();
}, },
methods: { methods: {
/** Show the dialog */
show() { show() {
this.modal.show(); this.modal.show();
}, },
/** Show dialog to confirm enabling 2FA */
confirmEnableTwoFA() { confirmEnableTwoFA() {
this.$refs.confirmEnableTwoFA.show(); this.$refs.confirmEnableTwoFA.show();
}, },
/** Show dialog to confirm disabling 2FA */
confirmDisableTwoFA() { confirmDisableTwoFA() {
this.$refs.confirmDisableTwoFA.show(); this.$refs.confirmDisableTwoFA.show();
}, },
/** Prepare 2FA configuration */
prepare2FA() { prepare2FA() {
this.processing = true; this.processing = true;
@ -126,6 +130,7 @@ export default {
}); });
}, },
/** Save the current 2FA configuration */
save2FA() { save2FA() {
this.processing = true; this.processing = true;
@ -143,6 +148,7 @@ export default {
}); });
}, },
/** Disable 2FA for this user */
disable2FA() { disable2FA() {
this.processing = true; this.processing = true;
@ -160,6 +166,7 @@ export default {
}); });
}, },
/** Verify the token generated by the user */
verifyToken() { verifyToken() {
this.$root.getSocket().emit("verifyToken", this.token, this.currentPassword, (res) => { this.$root.getSocket().emit("verifyToken", this.token, this.currentPassword, (res) => {
if (res.ok) { if (res.ok) {
@ -170,6 +177,7 @@ export default {
}); });
}, },
/** Get current status of 2FA */
getStatus() { getStatus() {
this.$root.getSocket().emit("twoFAStatus", (res) => { this.$root.getSocket().emit("twoFAStatus", (res) => {
if (res.ok) { if (res.ok) {

View file

@ -5,14 +5,17 @@
<script> <script>
export default { export default {
props: { props: {
/** Monitor this represents */
monitor: { monitor: {
type: Object, type: Object,
default: null, default: null,
}, },
/** Type of monitor */
type: { type: {
type: String, type: String,
default: null, default: null,
}, },
/** Is this a pill? */
pill: { pill: {
type: Boolean, type: Boolean,
default: false, default: false,

View file

@ -0,0 +1,13 @@
<template>
<div class="mb-3">
<label for="alertnow-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="alertnow-webhook-url" v-model="$parent.notification.alertNowWebhookURL" type="text" class="form-control" required>
<div class="form-text">
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
<a href="https://service.opsnow.com/docs/alertnow/en/user-guide-alertnow-en.html#standard" target="_blank">{{ $t("here") }}</a>
</i18n-t>
</div>
</div>
</template>

View file

@ -1,38 +1,39 @@
import STMP from "./SMTP.vue"; import Alerta from "./Alerta.vue";
import Telegram from "./Telegram.vue"; import AlertNow from "./AlertNow.vue";
import AliyunSMS from "./AliyunSms.vue";
import Apprise from "./Apprise.vue";
import Bark from "./Bark.vue";
import ClickSendSMS from "./ClickSendSMS.vue";
import DingDing from "./DingDing.vue";
import Discord from "./Discord.vue"; import Discord from "./Discord.vue";
import Webhook from "./Webhook.vue"; import Feishu from "./Feishu.vue";
import Signal from "./Signal.vue"; import GoogleChat from "./GoogleChat.vue";
import Gorush from "./Gorush.vue";
import Gotify from "./Gotify.vue"; import Gotify from "./Gotify.vue";
import Line from "./Line.vue";
import LunaSea from "./LunaSea.vue";
import Matrix from "./Matrix.vue";
import Mattermost from "./Mattermost.vue";
import Ntfy from "./Ntfy.vue"; import Ntfy from "./Ntfy.vue";
import Slack from "./Slack.vue"; import Octopush from "./Octopush.vue";
import RocketChat from "./RocketChat.vue"; import OneBot from "./OneBot.vue";
import Teams from "./Teams.vue"; import PagerDuty from "./PagerDuty.vue";
import PromoSMS from "./PromoSMS.vue";
import Pushbullet from "./Pushbullet.vue";
import PushDeer from "./PushDeer.vue";
import Pushover from "./Pushover.vue"; import Pushover from "./Pushover.vue";
import Pushy from "./Pushy.vue"; import Pushy from "./Pushy.vue";
import TechulusPush from "./TechulusPush.vue"; import RocketChat from "./RocketChat.vue";
import Octopush from "./Octopush.vue";
import PromoSMS from "./PromoSMS.vue";
import ClickSendSMS from "./ClickSendSMS.vue";
import LunaSea from "./LunaSea.vue";
import Feishu from "./Feishu.vue";
import Apprise from "./Apprise.vue";
import Pushbullet from "./Pushbullet.vue";
import Line from "./Line.vue";
import Mattermost from "./Mattermost.vue";
import Matrix from "./Matrix.vue";
import AliyunSMS from "./AliyunSms.vue";
import DingDing from "./DingDing.vue";
import Bark from "./Bark.vue";
import SerwerSMS from "./SerwerSMS.vue"; import SerwerSMS from "./SerwerSMS.vue";
import Signal from "./Signal.vue";
import Slack from "./Slack.vue";
import Stackfield from "./Stackfield.vue"; import Stackfield from "./Stackfield.vue";
import STMP from "./SMTP.vue";
import Teams from "./Teams.vue";
import TechulusPush from "./TechulusPush.vue";
import Telegram from "./Telegram.vue";
import Webhook from "./Webhook.vue";
import WeCom from "./WeCom.vue"; import WeCom from "./WeCom.vue";
import GoogleChat from "./GoogleChat.vue";
import PagerDuty from "./PagerDuty.vue";
import Gorush from "./Gorush.vue";
import Alerta from "./Alerta.vue";
import OneBot from "./OneBot.vue";
import PushDeer from "./PushDeer.vue";
/** /**
* Manage all notification form. * Manage all notification form.
@ -40,41 +41,42 @@ import PushDeer from "./PushDeer.vue";
* @type { Record<string, any> } * @type { Record<string, any> }
*/ */
const NotificationFormList = { const NotificationFormList = {
"telegram": Telegram, "alerta": Alerta,
"webhook": Webhook, "AlertNow": AlertNow,
"smtp": STMP,
"discord": Discord,
"teams": Teams,
"signal": Signal,
"gotify": Gotify,
"ntfy": Ntfy,
"slack": Slack,
"rocket.chat": RocketChat,
"pushover": Pushover,
"pushy": Pushy,
"PushByTechulus": TechulusPush,
"octopush": Octopush,
"promosms": PromoSMS,
"clicksendsms": ClickSendSMS,
"lunasea": LunaSea,
"Feishu": Feishu,
"AliyunSMS": AliyunSMS, "AliyunSMS": AliyunSMS,
"apprise": Apprise, "apprise": Apprise,
"pushbullet": Pushbullet,
"line": Line,
"mattermost": Mattermost,
"matrix": Matrix,
"DingDing": DingDing,
"Bark": Bark, "Bark": Bark,
"serwersms": SerwerSMS, "clicksendsms": ClickSendSMS,
"stackfield": Stackfield, "DingDing": DingDing,
"WeCom": WeCom, "discord": Discord,
"Feishu": Feishu,
"GoogleChat": GoogleChat, "GoogleChat": GoogleChat,
"PagerDuty": PagerDuty,
"gorush": Gorush, "gorush": Gorush,
"alerta": Alerta, "gotify": Gotify,
"line": Line,
"lunasea": LunaSea,
"matrix": Matrix,
"mattermost": Mattermost,
"ntfy": Ntfy,
"octopush": Octopush,
"OneBot": OneBot, "OneBot": OneBot,
"PagerDuty": PagerDuty,
"promosms": PromoSMS,
"pushbullet": Pushbullet,
"PushByTechulus": TechulusPush,
"PushDeer": PushDeer, "PushDeer": PushDeer,
"pushover": Pushover,
"pushy": Pushy,
"rocket.chat": RocketChat,
"serwersms": SerwerSMS,
"signal": Signal,
"slack": Slack,
"smtp": STMP,
"stackfield": Stackfield,
"teams": Teams,
"telegram": Telegram,
"webhook": Webhook,
"WeCom": WeCom,
}; };
export default NotificationFormList; export default NotificationFormList;

View file

@ -4,6 +4,11 @@
<object class="my-4" width="200" height="200" data="/icon.svg" /> <object class="my-4" width="200" height="200" data="/icon.svg" />
<div class="fs-4 fw-bold">Uptime Kuma</div> <div class="fs-4 fw-bold">Uptime Kuma</div>
<div>{{ $t("Version") }}: {{ $root.info.version }}</div> <div>{{ $t("Version") }}: {{ $root.info.version }}</div>
<div class="frontend-version">{{ $t("Frontend Version") }}: {{ $root.frontendVersion }}</div>
<div v-if="!$root.isFrontendBackendVersionMatched" class="alert alert-warning mt-4" role="alert">
{{ $t("Frontend Version do not match backend version!") }}
</div>
<div class="my-3 update-link"><a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">{{ $t("Check Update On GitHub") }}</a></div> <div class="my-3 update-link"><a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">{{ $t("Check Update On GitHub") }}</a></div>
@ -46,6 +51,16 @@ export default {
} }
.update-link { .update-link {
font-size: 0.9em; font-size: 0.8em;
} }
.frontend-version {
font-size: 0.9em;
color: #cccccc;
.dark & {
color: #333333;
}
}
</style> </style>

View file

@ -133,10 +133,15 @@ export default {
}, },
methods: { methods: {
/**
* Show the confimation dialog confirming the configuration
* be imported
*/
confirmImport() { confirmImport() {
this.$refs.confirmImport.show(); this.$refs.confirmImport.show();
}, },
/** Download a backup of the configuration */
downloadBackup() { downloadBackup() {
let time = dayjs().format("YYYY_MM_DD-hh_mm_ss"); let time = dayjs().format("YYYY_MM_DD-hh_mm_ss");
let fileName = `Uptime_Kuma_Backup_${time}.json`; let fileName = `Uptime_Kuma_Backup_${time}.json`;
@ -157,6 +162,10 @@ export default {
downloadItem.click(); downloadItem.click();
}, },
/**
* Import the specified backup file
* @returns {?string}
*/
importBackup() { importBackup() {
this.processing = true; this.processing = true;
let uploadItem = document.getElementById("import-backend").files; let uploadItem = document.getElementById("import-backend").files;

View file

@ -178,10 +178,12 @@ export default {
}, },
methods: { methods: {
/** Save the settings */
saveGeneral() { saveGeneral() {
localStorage.timezone = this.$root.userTimezone; localStorage.timezone = this.$root.userTimezone;
this.saveSettings(); this.saveSettings();
}, },
/** Get the base URL of the application */
autoGetPrimaryBaseURL() { autoGetPrimaryBaseURL() {
this.settings.primaryBaseURL = location.protocol + "//" + location.host; this.settings.primaryBaseURL = location.protocol + "//" + location.host;
}, },

View file

@ -90,6 +90,7 @@ export default {
}, },
methods: { methods: {
/** Get the current size of the database */
loadDatabaseSize() { loadDatabaseSize() {
log.debug("monitorhistory", "load database size"); log.debug("monitorhistory", "load database size");
this.$root.getSocket().emit("getDatabaseSize", (res) => { this.$root.getSocket().emit("getDatabaseSize", (res) => {
@ -102,6 +103,7 @@ export default {
}); });
}, },
/** Request that the database is shrunk */
shrinkDatabase() { shrinkDatabase() {
this.$root.getSocket().emit("shrinkDatabase", (res) => { this.$root.getSocket().emit("shrinkDatabase", (res) => {
if (res.ok) { if (res.ok) {
@ -113,10 +115,12 @@ export default {
}); });
}, },
/** Show the dialog to confirm clearing stats */
confirmClearStatistics() { confirmClearStatistics() {
this.$refs.confirmClearStatistics.show(); this.$refs.confirmClearStatistics.show();
}, },
/** Send the request to clear stats */
clearStatistics() { clearStatistics() {
this.$root.clearStatistics((res) => { this.$root.clearStatistics((res) => {
if (res.ok) { if (res.ok) {

View file

@ -23,6 +23,7 @@
<div class="my-4 pt-4"> <div class="my-4 pt-4">
<h5 class="my-4 settings-subheading">{{ $t("settingsCertificateExpiry") }}</h5> <h5 class="my-4 settings-subheading">{{ $t("settingsCertificateExpiry") }}</h5>
<p>{{ $t("certificationExpiryDescription") }}</p> <p>{{ $t("certificationExpiryDescription") }}</p>
<p>{{ $t("notificationDescription") }}</p>
<div class="mt-1 mb-3 ps-2 cert-exp-days col-12 col-xl-6"> <div class="mt-1 mb-3 ps-2 cert-exp-days col-12 col-xl-6">
<div v-for="day in settings.tlsExpiryNotifyDays" :key="day" class="d-flex align-items-center justify-content-between cert-exp-day-row py-2"> <div v-for="day in settings.tlsExpiryNotifyDays" :key="day" class="d-flex align-items-center justify-content-between cert-exp-day-row py-2">
<span>{{ day }} {{ $tc("day", day) }}</span> <span>{{ day }} {{ $tc("day", day) }}</span>

View file

@ -68,7 +68,9 @@
<Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop"> <Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop">
{{ $t("The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.") }} {{ $t("The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.") }}
<div class="mt-3"> <p class="mt-2">{{ $t("disableCloudflaredNoAuthMsg") }}</p>
<div v-if="!settings.disableAuth" class="mt-3">
<label for="current-password2" class="form-label"> <label for="current-password2" class="form-label">
{{ $t("Current Password") }} {{ $t("Current Password") }}
</label> </label>
@ -108,7 +110,9 @@ export default {
return this.$root.cloudflared; return this.$root.cloudflared;
}, },
computed: { computed: {
settings() {
return this.$parent.$parent.$parent.settings;
},
}, },
watch: { watch: {
@ -120,14 +124,17 @@ export default {
this.$root.getSocket().emit(prefix + "leave"); this.$root.getSocket().emit(prefix + "leave");
}, },
methods: { methods: {
/** Start the Cloudflare tunnel */
start() { start() {
this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken); this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken);
}, },
/** Stop the Cloudflare tunnel */
stop() { stop() {
this.$root.getSocket().emit(prefix + "stop", this.currentPassword, (res) => { this.$root.getSocket().emit(prefix + "stop", this.currentPassword, (res) => {
this.$root.toastRes(res); this.$root.toastRes(res);
}); });
}, },
/** Remove the token for the Cloudflare tunnel */
removeToken() { removeToken() {
this.$root.getSocket().emit(prefix + "removeToken"); this.$root.getSocket().emit(prefix + "removeToken");
this.cloudflareTunnelToken = ""; this.cloudflareTunnelToken = "";

View file

@ -90,162 +90,11 @@
<TwoFADialog ref="TwoFADialog" /> <TwoFADialog ref="TwoFADialog" />
<Confirm ref="confirmDisableAuth" btn-style="btn-danger" :yes-text="$t('I understand, please disable')" :no-text="$t('Leave')" @yes="disableAuth"> <Confirm ref="confirmDisableAuth" btn-style="btn-danger" :yes-text="$t('I understand, please disable')" :no-text="$t('Leave')" @yes="disableAuth">
<template v-if="$i18n.locale === 'es-ES' "> <!-- eslint-disable-next-line vue/no-v-html -->
<p>Seguro que deseas <strong>deshabilitar la autenticación</strong>?</p> <p v-html="$t('disableauth.message1')"></p>
<p>Es para <strong>quien implementa autenticación de terceros</strong> ante Uptime Kuma como por ejemplo Cloudflare Access.</p> <!-- eslint-disable-next-line vue/no-v-html -->
<p>Por favor usar con cuidado.</p> <p v-html="$t('disableauth.message2')"></p>
</template> <p>{{ $t("Please use this option carefully!") }}</p>
<template v-else-if="$i18n.locale === 'pt-BR' ">
<p>Você tem certeza que deseja <strong>desativar a autenticação</strong>?</p>
<p>Isso é para <strong>alguém que tem autenticação de terceiros</strong> na frente do 'UpTime Kuma' como o Cloudflare Access.</p>
<p>Por favor, utilize isso com cautela.</p>
</template>
<template v-else-if="$i18n.locale === 'zh-HK' ">
<p>你是否確認<strong>取消登入認証</strong></p>
<p>這個功能是設計給已有<strong>第三方認証</strong>的用家例如 Cloudflare Access</p>
<p>請小心使用</p>
</template>
<template v-else-if="$i18n.locale === 'zh-CN' ">
<p>是否确定 <strong>取消登录验证</strong></p>
<p>这是为 <strong>有第三方认证</strong> 的用户提供的功能 Cloudflare Access</p>
<p>请谨慎使用</p>
</template>
<template v-else-if="$i18n.locale === 'zh-TW' ">
<p>你是否要<strong>取消登入驗證</strong></p>
<p>此功能是設計給已有<strong>第三方認證</strong>的使用者例如 Cloudflare Access</p>
<p>請謹慎使用</p>
</template>
<template v-else-if="$i18n.locale === 'de-DE' ">
<p>Bist du sicher das du die <strong>Authentifizierung deaktivieren</strong> möchtest?</p>
<p>Es ist für <strong>jemanden der eine externe Authentifizierung</strong> vor Uptime Kuma geschaltet hat, wie z.B. Cloudflare Access.</p>
<p>Bitte mit Vorsicht nutzen.</p>
</template>
<template v-else-if="$i18n.locale === 'sl-SI' ">
<p>Ali ste prepričani, da želite onemogočiti <strong>avtentikacijo</strong>?</p>
<p>Namenjen je <strong>nekomu, ki ima pred programom Uptime Kuma vklopljeno zunanje preverjanje pristnosti</strong>, na primer Cloudflare Access.</p>
<p>Uporabljajte previdno.</p>
</template>
<template v-else-if="$i18n.locale === 'sr' ">
<p>Да ли сте сигурни да желите да <strong>искључите аутентификацију</strong>?</p>
<p>То је за <strong>оне који имају додату аутентификацију</strong> испред Uptime Kuma као на пример Cloudflare Access.</p>
<p>Молим Вас користите ово са пажњом.</p>
</template>
<template v-else-if="$i18n.locale === 'sr-latn' ">
<p>Da li ste sigurni da želite da <strong>isključite autentifikaciju</strong>?</p>
<p>To je za <strong>one koji imaju dodatu autentifikaciju</strong> ispred Uptime Kuma kao na primer Cloudflare Access.</p>
<p>Molim Vas koristite ovo sa pažnjom.</p>
</template>
<template v-if="$i18n.locale === 'hr-HR' ">
<p>Jeste li sigurni da želite <strong>isključiti autentikaciju</strong>?</p>
<p>To je za <strong>korisnike koji imaju vanjsku autentikaciju stranice</strong> ispred Uptime Kume, poput usluge Cloudflare Access.</p>
<p>Pažljivo koristite ovu opciju.</p>
</template>
<template v-else-if="$i18n.locale === 'tr-TR' ">
<p><strong>Şifreli girişi devre dışı bırakmak istediğinizden</strong>emin misiniz?</p>
<p>Bu, Uptime Kuma'nın önünde Cloudflare Access gibi <strong>üçüncü taraf yetkilendirmesi olan</strong> kişiler içindir.</p>
<p>Lütfen dikkatli kullanın.</p>
</template>
<template v-else-if="$i18n.locale === 'ko-KR' ">
<p>정말로 <strong>인증 기능을 끌까요</strong>?</p>
<p> 기능은 <strong>Cloudflare Access와 같은 서드파티 인증</strong> Uptime Kuma 앞에 사용자를 위한 기능이에요.</p>
<p>신중하게 사용하세요.</p>
</template>
<template v-else-if="$i18n.locale === 'pl' ">
<p>Czy na pewno chcesz <strong>wyłączyć autoryzację</strong>?</p>
<p>Jest przeznaczony dla <strong>kogoś, kto ma autoryzację zewnętrzną</strong> przed Uptime Kuma, taką jak Cloudflare Access.</p>
<p>Proszę używać ostrożnie.</p>
</template>
<template v-else-if="$i18n.locale === 'et-EE' ">
<p>Kas soovid <strong>lülitada autentimise välja</strong>?</p>
<p>Kastuamiseks <strong>välise autentimispakkujaga</strong>, näiteks Cloudflare Access.</p>
<p>Palun kasuta vastutustundlikult.</p>
</template>
<template v-else-if="$i18n.locale === 'it-IT' ">
<p><strong>Disabilitare l'autenticazione?</strong></p>
<p><strong>Questa opzione è per chi un sistema di autenticazione gestito da terze parti</strong> messo davanti ad Uptime Kuma, ad esempio Cloudflare Access.</p>
<p>Utilizzare con attenzione!</p>
</template>
<template v-else-if="$i18n.locale === 'id-ID' ">
<p>Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>?</p>
<p>Ini untuk <strong>mereka yang memiliki autentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare.</p>
<p>Gunakan dengan hati-hati.</p>
</template>
<template v-else-if="$i18n.locale === 'ru-RU' ">
<p>Вы уверены, что хотите <strong>отключить авторизацию</strong>?</p>
<p>Это подходит для <strong>тех, у кого стоит другая авторизация</strong> перед открытием Uptime Kuma, например Cloudflare Access.</p>
<p>Пожалуйста, используйте с осторожностью.</p>
</template>
<template v-else-if="$i18n.locale === 'uk-UA' ">
<p>Ви впевнені, що бажаєте <strong>вимкнути авторизацію</strong>?</p>
<p>Це підходить для <strong>тих, у кого встановлена інша авторизація</strong> пееред відкриттям Uptime Kuma, наприклад Cloudflare Access.</p>
<p>Будь ласка, використовуйте з обережністю.</p>
</template>
<template v-else-if="$i18n.locale === 'fa' ">
<p>آیا مطمئن هستید که میخواهید <strong>احراز هویت را غیر فعال کنید</strong>?</p>
<p>این ویژگی برای کسانی است که <strong> لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کردهاند</strong>، مانند Cloudflare Access.</p>
<p>لطفا از این امکان با دقت استفاده کنید.</p>
</template>
<template v-else-if="$i18n.locale === 'bg-BG' ">
<p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p>
<p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access, Authelia или друг механизъм за удостоверяване.</p>
<p>Моля, използвайте с повишено внимание.</p>
</template>
<template v-else-if="$i18n.locale === 'hu' ">
<p>Biztos benne, hogy <strong>kikapcsolja a hitelesítést</strong>?</p>
<p>Akkor érdemes, ha <strong>van 3rd-party hitelesítés</strong> az Uptime Kuma-t megelőzően mint a Cloudflare Access.</p>
<p>Használja megfontoltan!</p>
</template>
<template v-else-if="$i18n.locale === 'nb-NO' ">
<p>Er du sikker at du vil <strong>deaktiver autentisering</strong>?</p>
<p>Dette er for <strong>de som har tredjepartsautorisering</strong> foran Uptime Kuma, for eksempel Cloudflare Access.</p>
<p>Vennligst vær forsiktig.</p>
</template>
<template v-else-if="$i18n.locale === 'cs-CZ' ">
<p>Opravdu chcete <strong>deaktivovat autentifikaci</strong>?</p>
<p>Tato možnost je určena pro případy, kdy <strong>máte autentifikaci zajištěnou třetí stranou</strong> ještě před přístupem do Uptime Kuma, například prostřednictvím Cloudflare Access.</p>
<p>Používejte ji prosím s rozmyslem.</p>
</template>
<template v-else-if="$i18n.locale === 'vi-VN' ">
<p>Bạn muốn <strong>TẮT XÁC THỰC</strong> không?</p>
<p>Điều này rất nguy hiểm<strong>BẤT KỲ AI</strong> cũng thể truy cập cướp quyền điều khiển.</p>
<p>Vui lòng <strong>cẩn thận</strong>.</p>
</template>
<template v-else-if="$i18n.locale === 'th-TH' ">
<p>ณตองการทจะ <strong>ดใชงานระบบรบรองความถกตองใชหรอไม</strong>?</p>
<p>ระบบนกออกแบบมาเพอการใชงานกบระบบรบรองความถกตองของบคคลทสามเช Cloudflare Access, Authelia หรอวการอ </p>
<p>โปรดใชความระมดระวงในการเลอกใชงานระบบน !</p>
</template>
<!-- English (en) -->
<template v-else>
<p>Are you sure want to <strong>disable authentication</strong>?</p>
<p>It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Uptime Kuma such as Cloudflare Access, Authelia or other authentication mechanisms.</p>
<p>Please use this option carefully!</p>
</template>
<div class="mb-3"> <div class="mb-3">
<label for="current-password2" class="form-label"> <label for="current-password2" class="form-label">
@ -303,6 +152,7 @@ export default {
}, },
methods: { methods: {
/** Check new passwords match before saving them */
savePassword() { savePassword() {
if (this.password.newPassword !== this.password.repeatNewPassword) { if (this.password.newPassword !== this.password.repeatNewPassword) {
this.invalidPassword = true; this.invalidPassword = true;
@ -320,6 +170,7 @@ export default {
} }
}, },
/** Disable authentication for web app access */
disableAuth() { disableAuth() {
this.settings.disableAuth = true; this.settings.disableAuth = true;
@ -332,6 +183,7 @@ export default {
}, this.password.currentPassword); }, this.password.currentPassword);
}, },
/** Enable authentication for web app access */
enableAuth() { enableAuth() {
this.settings.disableAuth = false; this.settings.disableAuth = false;
this.saveSettings(); this.saveSettings();

View file

@ -9,6 +9,7 @@ const languageList = {
"nl-NL": "Nederlands", "nl-NL": "Nederlands",
"nb-NO": "Norsk", "nb-NO": "Norsk",
"es-ES": "Español", "es-ES": "Español",
"eu": "Euskara",
"fa": "Farsi", "fa": "Farsi",
"pt-BR": "Português (Brasileiro)", "pt-BR": "Português (Brasileiro)",
"fr-FR": "Français (France)", "fr-FR": "Français (France)",

View file

@ -81,6 +81,7 @@ library.add(
faUndo, faUndo,
faPlusCircle, faPlusCircle,
faAngleDown, faAngleDown,
faLink,
); );
export { FontAwesomeIcon }; export { FontAwesomeIcon };

View file

@ -4,8 +4,7 @@
2. Create a language file (e.g. `zh-TW.js`). The filename must be ISO language code: http://www.lingoes.net/en/translator/langcode.htm 2. Create a language file (e.g. `zh-TW.js`). The filename must be ISO language code: http://www.lingoes.net/en/translator/langcode.htm
3. Run `npm run update-language-files`. You can also use this command to check if there are new strings to translate for your language. 3. Run `npm run update-language-files`. You can also use this command to check if there are new strings to translate for your language.
4. Your language file should be filled in. You can translate now. 4. Your language file should be filled in. You can translate now.
5. Translate `src/components/settings/Security.vue` (search for a `Confirm` component with `rel="confirmDisableAuth"`). 5. Add it into `languageList` constant.
6. Add it into `languageList` constant. 6. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done.
7. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done.
If you do not have programming skills, let me know in [the issues section](https://github.com/louislam/uptime-kuma/issues). I will assist you. 😏 If you do not have programming skills, let me know in [the issues section](https://github.com/louislam/uptime-kuma/issues). I will assist you. 😏

View file

@ -89,13 +89,16 @@ export default {
"Search Engine Visibility": "Видимост за търсачки", "Search Engine Visibility": "Видимост за търсачки",
"Allow indexing": "Разреши индексиране", "Allow indexing": "Разреши индексиране",
"Discourage search engines from indexing site": "Не позволявай на търсачките да индексират този сайт", "Discourage search engines from indexing site": "Не позволявай на търсачките да индексират този сайт",
"Change Password": "Промени парола", "Change Password": "Промяна на парола",
"Current Password": "Текуща парола", "Current Password": "Текуща парола",
"New Password": "Нова парола", "New Password": "Нова парола",
"Repeat New Password": "Повторете новата парола", "Repeat New Password": "Повторете новата парола",
"Update Password": "Актуализирай парола", "Update Password": "Актуализирай паролата",
"Disable Auth": "Изключи удостоверяване", "Disable Auth": "Изключи удостоверяване",
"Enable Auth": "Включи удостоверяване", "Enable Auth": "Включи удостоверяване",
"disableauth.message1": "Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?",
"disableauth.message2": "Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access, Authelia или друг механизъм за удостоверяване.",
"Please use this option carefully!": "Моля, използвайте с повишено внимание.",
Logout: "Изход от профила", Logout: "Изход от профила",
Leave: "Отказ", Leave: "Отказ",
"I understand, please disable": "Разбирам. Моля, изключи", "I understand, please disable": "Разбирам. Моля, изключи",
@ -144,7 +147,7 @@ export default {
"Setup 2FA": "Настройка 2FA", "Setup 2FA": "Настройка 2FA",
"Enable 2FA": "Включи 2FA", "Enable 2FA": "Включи 2FA",
"Disable 2FA": "Изключи 2FA", "Disable 2FA": "Изключи 2FA",
"2FA Settings": "Настройки 2FA", "2FA Settings": "Настройка за 2FA",
"Two Factor Authentication": "Двуфакторно удостоверяване", "Two Factor Authentication": "Двуфакторно удостоверяване",
Active: "Активно", Active: "Активно",
Inactive: "Неактивно", Inactive: "Неактивно",
@ -298,7 +301,7 @@ 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 ключ тук: ",
@ -307,12 +310,12 @@ export default {
PasswordsDoNotMatch: "Паролите не съвпадат.", PasswordsDoNotMatch: "Паролите не съвпадат.",
"Current User": "Текущ потребител", "Current User": "Текущ потребител",
recent: "Скорошни", recent: "Скорошни",
shrinkDatabaseDescription: "Инициира \"VACUUM\" за \"SQLite\" база данни. Ако Вашата база данни е създадена след версия 1.10.0, \"AUTO_VACUUM\" функцията е активна и това действие не нужно.", shrinkDatabaseDescription: "Инициира \"VACUUM\" за \"SQLite\" база данни. Ако Вашата база данни е създадена след версия 1.10.0, \"AUTO_VACUUM\" функцията е активна и това действие не е нужно.",
Done: "Готово", Done: "Готово",
Info: "Информация", Info: "Информация",
Security: "Сигурност", Security: "Сигурност",
"Steam API Key": "Steam API ключ", "Steam API Key": "Steam API ключ",
"Shrink Database": "Редуциране база данни", "Shrink Database": "Редуцирай базата данни",
"Pick a RR-Type...": "Изберете вида на ресурсния запис за мониторитане...", "Pick a RR-Type...": "Изберете вида на ресурсния запис за мониторитане...",
"Pick Accepted Status Codes...": "Изберете статус кодове, които да се считат за успешен отговор...", "Pick Accepted Status Codes...": "Изберете статус кодове, които да се считат за успешен отговор...",
Default: "По подразбиране", Default: "По подразбиране",
@ -421,6 +424,7 @@ export default {
Next: "Следващ", Next: "Следващ",
"The slug is already taken. Please choose another slug.": "Този слъг вече се използва. Моля изберете друг.", "The slug is already taken. Please choose another slug.": "Този слъг вече се използва. Моля изберете друг.",
"No Proxy": "Без прокси", "No Proxy": "Без прокси",
Authentication: "Удостоверяване",
"HTTP Basic Auth": "HTTP основно удостоверяване", "HTTP Basic Auth": "HTTP основно удостоверяване",
"New Status Page": "Нова статус страница", "New Status Page": "Нова статус страница",
"Page Not Found": "Страницата не е открита", "Page Not Found": "Страницата не е открита",
@ -516,16 +520,20 @@ export default {
wayToGetClickSendSMSToken: "Може да получите API потребителско име и API ключ от {0} .", wayToGetClickSendSMSToken: "Може да получите API потребителско име и API ключ от {0} .",
dnsPortDescription: "DNS порт на сървъра. По подразбиране е 53, но може да бъде променен по всяко време.", dnsPortDescription: "DNS порт на сървъра. По подразбиране е 53, но може да бъде променен по всяко време.",
error: "грешка", error: "грешка",
critical: "критична", critical: "критично",
wayToGetPagerDutyKey: "Може да го получите като посетите Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Тук може да потърсите \"Events API V2\". Повече информация {0}", wayToGetPagerDutyKey: "Може да го получите като посетите Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Тук трябва да потърсите \"Events API V2\". Повече информация {0}",
"Integration Key": "Ключ за интегриране", "Integration Key": "Ключ за интегриране",
"Integration URL": "URL адрес за интеграция", "Integration URL": "URL адрес за интеграция",
"Auto resolve or acknowledged": "Автоматично разрешаване или потвърждаване", "Auto resolve or acknowledged": "Автоматично разрешаване или потвърждаване",
"do nothing": "не прави нищо", "do nothing": "не прави нищо",
"auto acknowledged": "автоматично потвърждаване", "auto acknowledged": "автоматично потвърждаване",
"auto resolve": "автоматично потвърждаване", "auto resolve": "автоматично разрешаване",
"Connection String": "Стринг за връзка", "Connection String": "Стринг за връзка",
Query: "Заявка", Query: "Заявка",
settingsCertificateExpiry: "Изтичане валидността на TLS сертификата", settingsCertificateExpiry: "Изтичане валидността на TLS сертификата",
certificationExpiryDescription: "HTTPS мониторите задействат известие при изтичане на TLS сертификата в:", certificationExpiryDescription: "HTTPS мониторите ще задействат известие, ако е наличен изтичащ TLS сертификат, през следващите:",
"ntfy Topic": "ntfy Тема",
Domain: "Домейн",
Workstation: "Работна станция",
disableCloudflaredNoAuthMsg: "Тъй като сте в режим \"No Auth mode\", парола не се изисква.",
}; };

View file

@ -100,6 +100,9 @@ export default {
"Update Password": "Aktualizovat heslo", "Update Password": "Aktualizovat heslo",
"Disable Auth": "Deaktivovat ověřování", "Disable Auth": "Deaktivovat ověřování",
"Enable Auth": "Povolit ověřování", "Enable Auth": "Povolit ověřování",
"disableauth.message1": "Opravdu chcete <strong>deaktivovat autentifikaci</strong>?",
"disableauth.message2": "Tato možnost je určena pro případy, kdy <strong>máte autentifikaci zajištěnou třetí stranou</strong> ještě před přístupem do Uptime Kuma, například prostřednictvím Cloudflare Access.",
"Please use this option carefully!": "Používejte ji prosím s rozmyslem.",
Logout: "Odhlášení", Logout: "Odhlášení",
Leave: "Odejít", Leave: "Odejít",
"I understand, please disable": "Rozumím, chci ji deaktivovat", "I understand, please disable": "Rozumím, chci ji deaktivovat",

View file

@ -77,6 +77,9 @@ export default {
"Update Password": "Passwort aktualisieren", "Update Password": "Passwort aktualisieren",
"Disable Auth": "Authentifizierung deaktivieren", "Disable Auth": "Authentifizierung deaktivieren",
"Enable Auth": "Authentifizierung aktivieren", "Enable Auth": "Authentifizierung aktivieren",
"disableauth.message1": "Bist du sicher das du die <strong>Authentifizierung deaktivieren</strong> möchtest?",
"disableauth.message2": "Es ist für <strong>jemanden der eine externe Authentifizierung</strong> vor Uptime Kuma geschaltet hat, wie z.B. Cloudflare Access.",
"Please use this option carefully!": "Bitte mit Vorsicht nutzen.",
Logout: "Ausloggen", Logout: "Ausloggen",
notificationDescription: "Benachrichtigungen müssen einem Monitor zugewiesen werden, damit diese funktionieren.", notificationDescription: "Benachrichtigungen müssen einem Monitor zugewiesen werden, damit diese funktionieren.",
Leave: "Verlassen", Leave: "Verlassen",
@ -421,6 +424,7 @@ export default {
Next: "Weiter", Next: "Weiter",
"The slug is already taken. Please choose another slug.": "Der Slug ist bereits in Verwendung. Bitte wähle einen anderen.", "The slug is already taken. Please choose another slug.": "Der Slug ist bereits in Verwendung. Bitte wähle einen anderen.",
"No Proxy": "Kein Proxy", "No Proxy": "Kein Proxy",
Authentication: "Authentifizierung",
"HTTP Basic Auth": "HTTP Basisauthentifizierung", "HTTP Basic Auth": "HTTP Basisauthentifizierung",
"New Status Page": "Neue Status-Seite", "New Status Page": "Neue Status-Seite",
"Page Not Found": "Seite nicht gefunden", "Page Not Found": "Seite nicht gefunden",

View file

@ -101,6 +101,9 @@ export default {
"Update Password": "Update Password", "Update Password": "Update Password",
"Disable Auth": "Disable Auth", "Disable Auth": "Disable Auth",
"Enable Auth": "Enable Auth", "Enable Auth": "Enable Auth",
"disableauth.message1": "Are you sure want to <strong>disable authentication</strong>?",
"disableauth.message2": "It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Uptime Kuma such as Cloudflare Access, Authelia or other authentication mechanisms.",
"Please use this option carefully!": "Please use this option carefully!",
Logout: "Logout", Logout: "Logout",
Leave: "Leave", Leave: "Leave",
"I understand, please disable": "I understand, please disable", "I understand, please disable": "I understand, please disable",
@ -438,6 +441,7 @@ export default {
Next: "Next", Next: "Next",
"The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.", "The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.",
"No Proxy": "No Proxy", "No Proxy": "No Proxy",
Authentication: "Authentication",
"HTTP Basic Auth": "HTTP Basic Auth", "HTTP Basic Auth": "HTTP Basic Auth",
"New Status Page": "New Status Page", "New Status Page": "New Status Page",
"Page Not Found": "Page Not Found", "Page Not Found": "Page Not Found",
@ -538,4 +542,8 @@ export default {
"Container Name / ID": "Container Name / ID", "Container Name / ID": "Container Name / ID",
"Docker Host": "Docker Host", "Docker Host": "Docker Host",
"Docker Hosts": "Docker Hosts" "Docker Hosts": "Docker Hosts"
"ntfy Topic": "ntfy Topic",
"Domain": "Domain",
"Workstation": "Workstation",
disableCloudflaredNoAuthMsg: "You are in No Auth mode, password is not require.",
}; };

View file

@ -84,6 +84,9 @@ export default {
"Update Password": "Actualizar contraseña", "Update Password": "Actualizar contraseña",
"Disable Auth": "Deshabilitar Autenticación", "Disable Auth": "Deshabilitar Autenticación",
"Enable Auth": "Habilitar Autenticación", "Enable Auth": "Habilitar Autenticación",
"disableauth.message1": "Seguro que deseas <strong>deshabilitar la autenticación</strong>?",
"disableauth.message2": "Es para <strong>quien implementa autenticación de terceros</strong> ante Uptime Kuma como por ejemplo Cloudflare Access.",
"Please use this option carefully!": "Por favor usar con cuidado.",
Logout: "Cerrar sesión", Logout: "Cerrar sesión",
Leave: "Salir", Leave: "Salir",
"I understand, please disable": "Entiendo, por favor deshabilitar", "I understand, please disable": "Entiendo, por favor deshabilitar",

View file

@ -87,6 +87,9 @@ export default {
"Update Password": "Uuenda salasõna", "Update Password": "Uuenda salasõna",
"Disable Auth": "Lülita autentimine välja", "Disable Auth": "Lülita autentimine välja",
"Enable Auth": "Lülita autentimine sisse", "Enable Auth": "Lülita autentimine sisse",
"disableauth.message1": "Kas soovid <strong>lülitada autentimise välja</strong>?",
"disableauth.message2": "Kastuamiseks <strong>välise autentimispakkujaga</strong>, näiteks Cloudflare Access.",
"Please use this option carefully!": "Palun kasuta vastutustundlikult.",
Logout: "Logi välja", Logout: "Logi välja",
Leave: "Lahku", Leave: "Lahku",
"I understand, please disable": "Olen tutvunud riskidega, lülita välja", "I understand, please disable": "Olen tutvunud riskidega, lülita välja",

539
src/languages/eu.js Normal file
View file

@ -0,0 +1,539 @@
export default {
languageName: "Euskara",
checkEverySecond: "Egiaztatu {0} segunduro",
retryCheckEverySecond: "Errepikatu {0} segunduro",
retriesDescription: "Zerbitzua erorita markatu eta jakinarazpena bidali aurretik egindako saiakera kopuru maximoa",
ignoreTLSError: "Ezikusiarena egin TLS/SSL erroreei HTTPS webguneetan",
upsideDownModeDescription: "Alderantzizkatu erortze egoera. Zerbitzua martxan badago, ERORITA markatuko du.",
maxRedirectDescription: "Jarraitu beharreko berbideratze kopuru maximoa. Jarri 0 berbideratzeak desgaitzeko.",
acceptedStatusCodesDescription: "Hautatu erantzun ona kontsideratzen diren egoera kodeak.",
passwordNotMatchMsg: "Errepikatutako pasahitza ez dator bat.",
notificationDescription: "Jakinarazpenak monitorizazio funtzio bati asignatu behar zaizkio.",
keywordDescription: "Bilatu gako-hitza HTML edo JSON erantzunean. Bilaketan maiuskulak kontuan hartzen dira.",
pauseDashboardHome: "Gelditu",
deleteMonitorMsg: "Ziur zaude monitorizazio hau ezabatu nahi duzula?",
deleteNotificationMsg: "Ziur zaude jakinarazpen hau monitorizazio guztientzat ezabatu nahi duzula?",
dnsPortDescription: "DNS zerbitzari portua. Defektuz 53. Nahi duzunean aldatu dezakezu portua.",
resolverserverDescription: "Cloudflare zerbitzari lehenetsia da. Edozein unetan alda dezakezu ebazteko zerbitzaria.",
rrtypeDescription: "Hautatu kontrolatu nahi duzun RR mota",
enableDefaultNotificationDescription: "Jakinarazpen hau monitore berrientzat gaituko da defektuz. Baina monitorizazio bakoitzarentzat jakinarazpena desgaitu dezakezu.",
pauseMonitorMsg: "Ziur zaude gelditu egin nahi duzula?",
clearEventsMsg: "Ziur zaude monitorizazio honen gertaera guztiak ezabatu nahi dituzula?",
clearHeartbeatsMsg: "Ziur zaude monitorizazio honen pultsu guztiak ezabatu nahi dituzula?",
confirmClearStatisticsMsg: "Ziur zaude estatistika GUZTIAK ezabatu nahi dituzula?",
importHandleDescription: "Aukeratu 'existitzen bada', izen bereko monitore edo jakinarazpen bakoitza saltatu nahi baduzu. Lehendik dauden kontrol eta jakinarazpen guztiak ezabatuko ditu 'Gainidatzi' aukerak.",
confirmImportMsg: "Ziur zaude segurtasun-kopia inportatu nahi duzula? Egiaztatu inportatzeko aukera zuzena hautatu duzula.",
twoFAVerifyLabel: "Sartu zure tokena 2FA egiaztatzeko:",
tokenValidSettingsMsg: "Tokenak balio du! Orain 2FA konfigurazioa gorde dezakezu.",
confirmEnableTwoFAMsg: "Ziur zaude 2FA gaitu nahi duzula?",
confirmDisableTwoFAMsg: "Ziur zaude 2FA desgaitu nahi duzula?",
Settings: "Ezarpenak",
Dashboard: "Arbela",
"New Update": "Eguneraketa berria",
Language: "Hizkuntza",
Appearance: "Itxura",
Theme: "Gaia",
General: "Orokorra",
"Primary Base URL": "Oinarrizkoa URL",
Version: "Bertsioa",
"Check Update On GitHub": "Egiaztatu eguneraketa GitHuben",
List: "Zerrenda",
Add: "Gehitu",
"Add New Monitor": "Gehitu monitorizazio berria",
"Quick Stats": "Estatistika azkarrak",
Up: "Erabilgarri",
Down: "Erorita",
Pending: "Zain",
Unknown: "Ezezaguna",
Pause: "Gelditu",
Name: "Izena",
Status: "Egoera",
DateTime: "Data eta ordua",
Message: "Mezua",
"No important events": "Gertaera garrantzitsurik ez",
Resume: "Jarraitu",
Edit: "Editatu",
Delete: "Ezabatu",
Current: "Unekoa",
Uptime: "Martxan",
"Cert Exp.": "Ziurtagiri iraun.",
day: "egun | egun",
"-day": "-egun",
hour: "ordua",
"-hour": "-ordu",
Response: "Erantzuna",
Ping: "Ping",
"Monitor Type": "Monitorizazio mota",
Keyword: "Gakohitza",
"Friendly Name": "Izen xumea",
URL: "URLa",
Hostname: "Ostalari izena",
Port: "Portua",
"Heartbeat Interval": "Pultsu interbaloak",
Retries: "Errepikapenak",
"Heartbeat Retry Interval": "Pultsu errepikatze interbaloak",
Advanced: "Aurreratua",
"Upside Down Mode": "Alderantzizkako modua",
"Max. Redirects": "Berbideratze max.",
"Accepted Status Codes": "Onartutako egoera kodeak",
"Push URL": "Push URLa",
needPushEvery: "URL hau {0} segunduro deitu beharko zenuke.",
pushOptionalParams: "Hautazko parametroak: {0}",
Save: "Gorde",
Notifications: "Jakinarazpenak",
"Not available, please setup.": "Ez dago eskuragarri, ezarri mesedez.",
"Setup Notification": "Ezarri jakinarazpenak",
Light: "Argia",
Dark: "Iluna",
Auto: "Auto",
"Theme - Heartbeat Bar": "Gaia - Pultsu barra",
Normal: "Normala",
Bottom: "Behean",
None: "Bat ere ez",
Timezone: "Timezone",
"Search Engine Visibility": "Bilatzaile ikurgarritasuna",
"Allow indexing": "Onartu indexatzea",
"Discourage search engines from indexing site": "Discourage search engines from indexing site",
"Change Password": "Aldatu pasahitza",
"Current Password": "Uneko pasahitza",
"New Password": "Pasahitz berria",
"Repeat New Password": "Errepikatu pasahitz berria",
"Update Password": "Eguneratu pasahitza",
"Disable Auth": "Desgaitu Auth",
"Enable Auth": "Gaitu Auth",
"disableauth.message1": "Ziur zaude <strong>autentifikazioa desgaitu</strong> nahi duzula?",
"disableauth.message2": "Egoera jakin batzuetarako diseinatuta dago, Uptime Kumaren <strong>aurrean hirugarrengo autentifikazio batzuek jartzeko</strong> (Cloudflare Access, Authelia edo beste autentifikazio-mekanismo batzuk).",
"Please use this option carefully!": "Mesedez, kontuz erabili aukera hau!",
Logout: "Saioa amaitu",
Leave: "Utzi",
"I understand, please disable": "Ulertzen dut, mesedez desgaitu",
Confirm: "Baieztatu",
Yes: "Bai",
No: "Ez",
Username: "Erabiltzailea",
Password: "Pasahitza",
"Remember me": "Gogora nazazu",
Login: "Saioa hasi",
"No Monitors, please": "Monitorizaziorik ez, mesedez",
"add one": "gehitu bat",
"Notification Type": "Jakinarazpen mota",
Email: "Emaila",
Test: "Testa",
"Certificate Info": "Ziurtagiri informazioa",
"Resolver Server": "Ebazpen-zerbitzaria",
"Resource Record Type": "Baliabideen erregistro mota",
"Last Result": "Azken emaitza",
"Create your admin account": "Sortu zure admin kontua",
"Repeat Password": "Errepikatu pasahitza",
"Import Backup": "segurtasun-kopia inportatu",
"Export Backup": "segurtasun-kopia esportatu",
Export: "Esportatu",
Import: "Inportatu",
respTime: "Erantz. denbora (ms)",
notAvailableShort: "N/A",
"Default enabled": "Lehenetsia gaituta",
"Apply on all existing monitors": "Aplikatu existitzen diren monitorizazio guztietan",
Create: "Sortu",
"Clear Data": "Garbitu datuak",
Events: "Gertaerak",
Heartbeats: "Pultsuak",
"Auto Get": "Auto Get",
backupDescription: "Monitore eta jakinarazpen guztien segurtasun-kopiak egin ditzakezu JSON fitxategi batean.",
backupDescription2: "Oharra: ez dira historia eta gertaeren datuak sartzen.",
backupDescription3: "Datu sentikorrak, hala nola jakinarazpen tokenak, esportazio-fitxategian sartzen dira; mesedez, gorde esportazioa modu seguruan.",
alertNoFile: "Mesedez hautatu inportatzeko fitxategia.",
alertWrongFileType: "Mesedez hautatu JSON fitxategia.",
"Clear all statistics": "Garbitu estatistika guztiak",
"Skip existing": "Saltatu existitzen bada",
Overwrite: "Gainidatzi",
Options: "Aukerak",
"Keep both": "Biak mantendu",
"Verify Token": "Egiaztatu Tokena",
"Setup 2FA": "Ezarri 2FA",
"Enable 2FA": "Gaitu 2FA",
"Disable 2FA": "Desgaitu 2FA",
"2FA Settings": "2FA ezarpenak",
"Two Factor Authentication": "Bi aldetako autentifikazioa (2FA)",
Active: "Aktibo",
Inactive: "Inaktibo",
Token: "Tokena",
"Show URI": "Erakutsi URIa",
Tags: "Etiketak",
"Add New below or Select...": "Gehitu beste bat behean edo hautatu...",
"Tag with this name already exist.": "Izen hau duen etiketa dagoeneko badago.",
"Tag with this value already exist.": "Balio hau duen etiketa dagoeneko badago.",
color: "kolorea",
"value (optional)": "balioa (hautazkoa)",
Gray: "Grisa",
Red: "Gorria",
Orange: "Naranja",
Green: "Berdea",
Blue: "Urdina",
Indigo: "Indigo",
Purple: "Morea",
Pink: "Arrosa",
"Search...": "Bilatu...",
"Avg. Ping": "Batazbesteko Pinga",
"Avg. Response": "Batazbesteko erantzuna",
"Entry Page": "Sarrera orria",
statusPageNothing: "Ezer ere ez hemen, mesedez gehitu taldea edo monitorizazioa.",
"No Services": "Zerbitzurik ez",
"All Systems Operational": "Sistema guztiak martxan",
"Partially Degraded Service": "Zerbitzu partzialki degradatua",
"Degraded Service": "Zerbitzu degradatua",
"Add Group": "Gehitu taldea",
"Add a monitor": "Gehitu monitorizazioa",
"Edit Status Page": "Editatu egoera orria",
"Go to Dashboard": "Joan arbelera",
"Status Page": "Egoera orria",
"Status Pages": "Egoera orriak",
defaultNotificationName: "Nire {notification} Alerta ({number})",
here: "Hemen",
Required: "Beharrezkoa",
telegram: "Telegram",
"Bot Token": "Bot Tokena",
wayToGetTelegramToken: "You can get a token from {0}.",
"Chat ID": "Txat IDa",
supportTelegramChatID: "Support Direct Chat / Group / Channel's Chat ID",
wayToGetTelegramChatID: "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:",
"YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE",
chatIDNotFound: "Chat ID is not found; please send a message to this bot first",
webhook: "Webhook",
"Post URL": "Bidalketa URLa",
"Content Type": "Eduki mota",
webhookJsonDesc: "{0} is good for any modern HTTP servers such as Express.js",
webhookFormDataDesc: "{multipart} is good for PHP. The JSON will need to be parsed with {decodeFunction}",
smtp: "Emaila (SMTP)",
secureOptionNone: "Bat ere ez / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Ignore TLS Error",
"From Email": "Email honetatik",
emailCustomSubject: "Pertsonalizatutako gaia",
"To Email": "Email honetara",
smtpCC: "CC",
smtpBCC: "BCC",
discord: "Discord",
"Discord Webhook URL": "Discord Webhook URL",
wayToGetDiscordURL: "You can get this by going to Server Settings -> Integrations -> Create Webhook",
"Bot Display Name": "Bot Display Name",
"Prefix Custom Message": "Prefix Custom Message",
"Hello @everyone is...": "Hello {'@'}everyone is...",
teams: "Microsoft Teams",
"Webhook URL": "Webhook URL",
wayToGetTeamsURL: "You can learn how to create a webhook URL {0}.",
signal: "Signal",
Number: "Zenbakia",
Recipients: "Recipients",
needSignalAPI: "You need to have a signal client with REST API.",
wayToCheckSignalURL: "You can check this URL to view how to set one up:",
signalImportant: "IMPORTANT: You cannot mix groups and numbers in recipients!",
gotify: "Gotify",
"Application Token": "Aplikazio tokena",
"Server URL": "Zerbitzari URLa",
Priority: "Lehentasuna",
slack: "Slack",
"Icon Emoji": "Emoji ikonoa",
"Channel Name": "Kanalaren izena",
"Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "More info about Webhooks on: {0}",
aboutChannelName: "Enter the channel name on {0} Channel Name field if you want to bypass the Webhook channel. Ex: #other-channel",
aboutKumaURL: "If you leave the Uptime Kuma URL field blank, it will default to the Project GitHub page.",
emojiCheatSheet: "Emoji cheat sheet: {0}",
"rocket.chat": "Rocket.Chat",
pushover: "Pushover",
pushy: "Pushy",
PushByTechulus: "Push by Techulus",
octopush: "Octopush",
promosms: "PromoSMS",
clicksendsms: "ClickSend SMS",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
GoogleChat: "Google Chat (Google Workspace only)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"User Key": "Erabiltzaile gakoa",
Device: "Gailua",
"Message Title": "Mezuaren izenburua",
"Notification Sound": "Jakinarazpen soinua",
"More info on:": "More info on: {0}",
pushoverDesc1: "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.",
pushoverDesc2: "If you want to send notifications to different devices, fill out Device field.",
"SMS Type": "SMS mota",
octopushTypePremium: "Premium (Fast - recommended for alerting)",
octopushTypeLowCost: "Low Cost (Slow - sometimes blocked by operator)",
checkPrice: "Check {0} prices:",
apiCredentials: "API credentials",
octopushLegacyHint: "Do you use the legacy version of Octopush (2011-2020) or the new version?",
"Check octopush prices": "Check octopush prices {0}.",
octopushPhoneNumber: "Phone number (intl format, eg : +33612345678) ",
octopushSMSSender: "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)",
"LunaSea Device ID": "LunaSea Device ID",
"Apprise URL": "Apprise URL",
"Example:": "Adibidez: {0}",
"Read more:": "Irakurri gehiago: {0}",
"Status:": "Egoera: {0}",
"Read more": "Irakurri gehiago",
appriseInstalled: "Apprise instalatuta.",
appriseNotInstalled: "Apprise ez dago instalatuta. {0}",
"Access Token": "Access Token",
"Channel access token": "Channel access token",
"Line Developers Console": "Line Developers Console",
lineDevConsoleTo: "Line Developers Console - {0}",
"Basic Settings": "Oinarrizko ezarpenak",
"User ID": "Erabiltzaile ID",
"Messaging API": "Messaging API",
wayToGetLineChannelToken: "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user ID from the above mentioned menu items.",
"Icon URL": "Ikono URL",
aboutIconURL: "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.",
aboutMattermostChannelName: "You can override the default channel that the Webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in the Mattermost Webhook settings. Ex: #other-channel",
matrix: "Matrix",
promosmsTypeEco: "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.",
promosmsTypeFlash: "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.",
promosmsTypeFull: "SMS FULL - Premium tier of SMS, You can use your Sender Name (You need to register name first). Reliable for alerts.",
promosmsTypeSpeed: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).",
promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)",
promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
"Feishu WebHookUrl": "Feishu WebHookURL",
matrixHomeserverURL: "Hasiera zerbitzari URL (with http(s):// and optionally port)",
"Internal Room Id": "Internal Room ID",
matrixDesc1: "You can find the internal room ID by looking in the advanced section of the room settings in your Matrix client. It should look like !QMdRCpUIfLwsfjxye6:home.server.",
matrixDesc2: "It is highly recommended you create a new user and do not use your own Matrix user's access token as it will allow full access to your account and all the rooms you joined. Instead, create a new user and only invite it to the room that you want to receive the notification in. You can get the access token by running {0}",
Method: "Metodoa",
Body: "Gorputza",
Headers: "Goiburuak",
PushUrl: "Push URL",
HeadersInvalidFormat: "The request headers are not valid JSON: ",
BodyInvalidFormat: "The request body is not valid JSON: ",
"Monitor History": "Monitorizazio Historia",
clearDataOlderThan: "Keep monitor history data for {0} days.",
PasswordsDoNotMatch: "Pasahitzak ez datoz bat.",
records: "records",
"One record": "One record",
steamApiKeyDescription: "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key here: ",
"Current User": "Uneko erabiltzailea",
topic: "Topic",
topicExplanation: "MQTT topic to monitor",
successMessage: "Arrakasta mezua",
successMessageExplanation: "MQTT message that will be considered as success",
recent: "Duela gutxikoa",
Done: "Egina",
Info: "Info",
Security: "Segurtasuna",
"Steam API Key": "Steam API Giltza",
"Shrink Database": "Shrink Datubasea",
"Pick a RR-Type...": "Pick a RR-Type...",
"Pick Accepted Status Codes...": "Hautatu onartutako egoera kodeak...",
Default: "Lehenetsia",
"HTTP Options": "HTTP Aukerak",
"Create Incident": "Sortu inzidentzia",
Title: "Titulua",
Content: "Edukia",
Style: "Estiloa",
info: "info",
warning: "kontuz",
danger: "arriskua",
error: "errorea",
critical: "kritikoa",
primary: "oinarrizkoa",
light: "argia",
dark: "iluna",
Post: "Post",
"Please input title and content": "Mesedez sartu titulua eta edukia",
Created: "Sortuta",
"Last Updated": "Azken eguneratzea",
Unpin: "Unpin",
"Switch to Light Theme": "Aldatu gai argira",
"Switch to Dark Theme": "Aldatu gai ilunera",
"Show Tags": "Erakutsi etiketak",
"Hide Tags": "Ezkutatu etiketak",
Description: "Deskribapena",
"No monitors available.": "Monitorizaziorik eskuragarri ez.",
"Add one": "Gehitu bat",
"No Monitors": "Monitorizaziorik ez",
"Untitled Group": "Titulurik gabeko taldea",
Services: "Zerbitzuak",
Discard: "Baztertu",
Cancel: "Ezeztatu",
"Powered by": "Honekin egina:",
shrinkDatabaseDescription: "Trigger database VACUUM for SQLite. If your database is created after 1.10.0, AUTO_VACUUM is already enabled and this action is not needed.",
serwersms: "SerwerSMS.pl",
serwersmsAPIUser: "API erabiltzailea (webapi_ aurre-hizkia barne)",
serwersmsAPIPassword: "API pasahitza",
serwersmsPhoneNumber: "Telefono zenbakia",
serwersmsSenderName: "SMS bidaltzaile izena (registered via customer portal)",
stackfield: "Stackfield",
Customize: "Pertsonalizatu",
"Custom Footer": "Oin pertsonalizatua",
"Custom CSS": "CSS pertsonalizatua",
smtpDkimSettings: "DKIM ezarpenak",
smtpDkimDesc: "Please refer to the Nodemailer DKIM {0} for usage.",
documentation: "dokumentazioa",
smtpDkimDomain: "Domeinu izena",
smtpDkimKeySelector: "Gako hautatzailea",
smtpDkimPrivateKey: "Gako pribatua",
smtpDkimHashAlgo: "Hash algoritmoa (hautazkoa)",
smtpDkimheaderFieldNames: "Header Keys to sign (Optional)",
smtpDkimskipFields: "Header Keys not to sign (Optional)",
wayToGetPagerDutyKey: "You can get this by going to Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Here you can search for \"Events API V2\". More info {0}",
"Integration Key": "Integration Key",
"Integration URL": "Integrazio URLa",
"Auto resolve or acknowledged": "Auto resolve or acknowledged",
"do nothing": "ez egin ezer",
"auto acknowledged": "auto acknowledged",
"auto resolve": "auto resolve",
gorush: "Gorush",
alerta: "Alerta",
alertaApiEndpoint: "API Endpoint",
alertaEnvironment: "Ingurunea",
alertaApiKey: "API Key",
alertaAlertState: "Alerta egoera",
alertaRecoverState: "Berreskuratze egoera",
deleteStatusPageMsg: "Ziur zaude egoera orri hau ezabatu nahi duzula?",
Proxies: "Proxiak",
default: "Lehenetsia",
enabled: "Gaituta",
setAsDefault: "Ezarri lehenetsitzat",
deleteProxyMsg: "Are you sure want to delete this proxy for all monitors?",
proxyDescription: "Proxies must be assigned to a monitor to function.",
enableProxyDescription: "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.",
setAsDefaultProxyDescription: "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.",
"Certificate Chain": "Certificate Chain",
Valid: "Baliozkoa",
Invalid: "Baliogabea",
AccessKeyId: "AccessKey ID",
SecretAccessKey: "AccessKey Secret",
PhoneNumbers: "TelefonoZenbakiak",
TemplateCode: "TemplateCode",
SignName: "SignName",
"Sms template must contain parameters: ": "Sms txantiloiak parametroak eduki behar ditu: ",
"Bark Endpoint": "Bark Endpoint",
WebHookUrl: "WebHookUrl",
SecretKey: "SecretKey",
"For safety, must use secret key": "For safety, must use secret key",
"Device Token": "Gailu tokena",
Platform: "Plataforma",
iOS: "iOS",
Android: "Android",
Huawei: "Huawei",
High: "Altua",
Retry: "Errepikatu",
Topic: "Gaia",
"WeCom Bot Key": "WeCom Bot Key",
"Setup Proxy": "Ezarri Proxya",
"Proxy Protocol": "Proxy protokoloa",
"Proxy Server": "Proxy zerbitzaria",
"Proxy server has authentication": "Proxy zerbitzariak autentifikazioa dauka",
User: "Erabiltzailea",
Installed: "Instalatuta",
"Not installed": "Instalatu gabe",
Running: "Martxan",
"Not running": "Ez martxan",
"Remove Token": "Ezabatu Tokena",
Start: "Hasi",
Stop: "Gelditu",
"Uptime Kuma": "Uptime Kuma",
"Add New Status Page": "Gehitu egoera orri berria",
Slug: "Sluga",
"Accept characters:": "Onartu karaktereak:",
startOrEndWithOnly: "Start or end with {0} only",
"No consecutive dashes": "No consecutive dashes",
Next: "Hurrengoa",
"The slug is already taken. Please choose another slug.": "Sluga dagoeneko hartuta dago. Mesedez beste bat hautatu.",
"No Proxy": "Proxyrik ez",
Authentication: "Authentication",
"HTTP Basic Auth": "HTTP oinarrizko Auth",
"New Status Page": "Egoera orri berria",
"Page Not Found": "Orria ez da aurkitu",
"Reverse Proxy": "Alderantzizkako Proxya",
Backup: "Backup",
About: "Honi buruz",
wayToGetCloudflaredURL: "(Download cloudflared from {0})",
cloudflareWebsite: "Cloudflare webgunea",
"Message:": "Mezua:",
"Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:",
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.",
"Other Software": "Beste softwarea",
"For example: nginx, Apache and Traefik.": "Adibidez: nginx, Apache and Traefik.",
"Please read": "Mesedez irakurri",
"Subject:": "Gaia:",
"Valid To:": "Balio-epea:",
"Days Remaining:": "Egun faltan:",
"Issuer:": "Issuer:",
"Fingerprint:": "Hatzmarka:",
"No status pages": "Egoera orririk ez",
"Domain Name Expiry Notification": "Domeinu izen iraungitze jakinarazpena",
Proxy: "Proxya",
"Date Created": "Data sortuta",
onebotHttpAddress: "OneBot HTTP helbidea",
onebotMessageType: "OneBot mezu mota",
onebotGroupMessage: "Taldea",
onebotPrivateMessage: "Pribatua",
onebotUserOrGroupId: "Talde/Erabiltzaile IDa",
onebotSafetyTips: "For safety, must set access token",
"PushDeer Key": "PushDeer Key",
"Footer Text": "Oineko testua",
"Show Powered By": "Erakutsi Honekin egina:",
"Domain Names": "Domeinu izenak",
signedInDisp: "Signed in as {0}",
signedInDispDisabled: "Auth desgaituta.",
"Certificate Expiry Notification": "Zertifikatu iraungitze jakinarazpena",
"API Username": "API Erabiltzailea",
"API Key": "API Gakoa",
"Recipient Number": "Recipient Number",
"From Name/Number": "From Name/Number",
"Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.",
"Octopush API Version": "Octopush API Version",
"Legacy Octopush-DM": "Legacy Octopush-DM",
endpoint: "endpoint",
octopushAPIKey: "\"API key\" from HTTP API credentials in control panel",
octopushLogin: "\"Login\" from HTTP API credentials in control panel",
promosmsLogin: "API Saio haste izena",
promosmsPassword: "API Pasahitza",
"pushoversounds pushover": "Pushover (defektuz)",
"pushoversounds bike": "Bizikleta",
"pushoversounds bugle": "Bugle",
"pushoversounds cashregister": "Cash Register",
"pushoversounds classical": "Klasikoa",
"pushoversounds cosmic": "Kosmikoa",
"pushoversounds falling": "Erortzen",
"pushoversounds gamelan": "Gamelan",
"pushoversounds incoming": "Incoming",
"pushoversounds intermission": "Intermission",
"pushoversounds magic": "Magia",
"pushoversounds mechanical": "Mekanikoa",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Sirena",
"pushoversounds spacealarm": "Espazio Alarma",
"pushoversounds tugboat": "Tug Boat",
"pushoversounds alien": "Alien Alarm (long)",
"pushoversounds climb": "Climb (long)",
"pushoversounds persistent": "Persistent (long)",
"pushoversounds echo": "Pushover Echo (long)",
"pushoversounds updown": "Up Down (long)",
"pushoversounds vibrate": "Bibrazioa soilik",
"pushoversounds none": "Bat ere ez (isilik)",
pushyAPIKey: "Secret API giltza",
pushyToken: "Gailu tokena",
"Show update if available": "Erakutsi eguneratzea eskuragarri badago",
"Also check beta release": "Beta bertsioak ere egiaztatu",
"Using a Reverse Proxy?": "Proxy alderantzizkako zerbitzaria erabiltzen?",
"Check how to config it for WebSocket": "Check how to config it for WebSocket",
"Steam Game Server": "Steam joko zerbitzaria",
"Most likely causes:": "Arrazoi probableenak:",
"The resource is no longer available.": "Baliabidea ez dago erabilgarri.",
"There might be a typing error in the address.": "Idazketa-akats bat egon daiteke helbidean.",
"What you can try:": "Probatu dezakezuna:",
"Retype the address.": "Berridatzi helbidea.",
"Go back to the previous page.": "Itzuli aurreko orrialdera",
"Coming Soon": "Laster",
wayToGetClickSendSMSToken: "API erabiltzailea and API giltza hemendik lortu ditzakezu: {0} .",
"Connection String": "Konexio katea",
Query: "Kontsulta",
settingsCertificateExpiry: "TLS irungitze zertifikatua",
certificationExpiryDescription: "HTTPS Monitorizazio jakinarazpena martxan jarri TLS zertifikatua iraungitzeko hau falta denean:",
"ntfy Topic": "ntfy Topic",
Domain: "Domeinua",
Workstation: "Lan gunea",
disableCloudflaredNoAuthMsg: "Ez Auth moduan zaude, pasahitza ez da beharrezkoa.",
};

View file

@ -96,6 +96,9 @@ export default {
"Update Password": "بروز رسانی رمز عبور", "Update Password": "بروز رسانی رمز عبور",
"Disable Auth": "غیر فعال سازی تایید هویت", "Disable Auth": "غیر فعال سازی تایید هویت",
"Enable Auth": "فعال سازی تایید هویت", "Enable Auth": "فعال سازی تایید هویت",
"disableauth.message1": "آیا مطمئن هستید که میخواهید <strong>احراز هویت را غیر فعال کنید</strong>?",
"disableauth.message2": "این ویژگی برای کسانی است که <strong> لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کرده‌اند</strong>، مانند Cloudflare Access.",
"Please use this option carefully!": "لطفا از این امکان با دقت استفاده کنید.",
Logout: "خروج", Logout: "خروج",
Leave: "منصرف شدم", Leave: "منصرف شدم",
"I understand, please disable": "متوجه هستم، لطفا غیرفعال کنید!", "I understand, please disable": "متوجه هستم، لطفا غیرفعال کنید!",

View file

@ -100,6 +100,9 @@ export default {
"Update Password": "Spremi novu lozinku", "Update Password": "Spremi novu lozinku",
"Disable Auth": "Onemogući autentikaciju", "Disable Auth": "Onemogući autentikaciju",
"Enable Auth": "Omogući autentikaciju", "Enable Auth": "Omogući autentikaciju",
"disableauth.message1": "Jeste li sigurni da želite <strong>isključiti autentikaciju</strong>?",
"disableauth.message2": "To je za <strong>korisnike koji imaju vanjsku autentikaciju stranice</strong> ispred Uptime Kume, poput usluge Cloudflare Access.",
"Please use this option carefully!": "Pažljivo koristite ovu opciju.",
Logout: "Odjava", Logout: "Odjava",
Leave: "Poništi", Leave: "Poništi",
"I understand, please disable": "Razumijem, svejedno onemogući", "I understand, please disable": "Razumijem, svejedno onemogući",

View file

@ -96,6 +96,9 @@ export default {
"Update Password": "Jelszó módosítása", "Update Password": "Jelszó módosítása",
"Disable Auth": "Hitelesítés tiltása", "Disable Auth": "Hitelesítés tiltása",
"Enable Auth": "Hitelesítés engedélyezése", "Enable Auth": "Hitelesítés engedélyezése",
"disableauth.message1": "Biztos benne, hogy <strong>kikapcsolja a hitelesítést</strong>?",
"disableauth.message2": "Akkor érdemes, ha <strong>van 3rd-party hitelesítés</strong> az Uptime Kuma-t megelőzően mint a Cloudflare Access.",
"Please use this option carefully!": "Használja megfontoltan!",
Logout: "Kijelentkezés", Logout: "Kijelentkezés",
Leave: "Elhagy", Leave: "Elhagy",
"I understand, please disable": "Megértettem, kérem tiltsa le", "I understand, please disable": "Megértettem, kérem tiltsa le",

View file

@ -96,6 +96,9 @@ export default {
"Update Password": "Perbarui Kata Sandi", "Update Password": "Perbarui Kata Sandi",
"Disable Auth": "Nonaktifkan Autentikasi", "Disable Auth": "Nonaktifkan Autentikasi",
"Enable Auth": "Aktifkan Autentikasi", "Enable Auth": "Aktifkan Autentikasi",
"disableauth.message1": "Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>?",
"disableauth.message2": "Ini untuk <strong>mereka yang memiliki autentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare.",
"Please use this option carefully!": "Gunakan dengan hati-hati.",
Logout: "Keluar", Logout: "Keluar",
Leave: "Pergi", Leave: "Pergi",
"I understand, please disable": "Saya mengerti, silakan dinonaktifkan", "I understand, please disable": "Saya mengerti, silakan dinonaktifkan",

View file

@ -100,6 +100,9 @@ export default {
"Update Password": "Modifica password", "Update Password": "Modifica password",
"Disable Auth": "Disabilita autenticazione", "Disable Auth": "Disabilita autenticazione",
"Enable Auth": "Abilita autenticazione", "Enable Auth": "Abilita autenticazione",
"disableauth.message1": "<strong>Disabilitare l'autenticazione?</strong>",
"disableauth.message2": "<strong>Questa opzione è per chi un sistema di autenticazione gestito da terze parti</strong> messo davanti ad Uptime Kuma, ad esempio Cloudflare Access.",
"Please use this option carefully!": "Utilizzare con attenzione!",
Logout: "Esci", Logout: "Esci",
Leave: "Annulla", Leave: "Annulla",
"I understand, please disable": "Lo capisco, disabilitare l'autenticazione.", "I understand, please disable": "Lo capisco, disabilitare l'autenticazione.",

View file

@ -96,6 +96,9 @@ export default {
"Update Password": "비밀번호 변경", "Update Password": "비밀번호 변경",
"Disable Auth": "인증 비활성화", "Disable Auth": "인증 비활성화",
"Enable Auth": "인증 활성화", "Enable Auth": "인증 활성화",
"disableauth.message1": "정말로 <strong>인증 기능을 끌까요</strong>?",
"disableauth.message2": "이 기능은 <strong>Cloudflare Access와 같은 서드파티 인증</strong>을 Uptime Kuma 앞에 둔 사용자를 위한 기능이에요.",
"Please use this option carefully!": "신중하게 사용하세요.",
Logout: "로그아웃", Logout: "로그아웃",
Leave: "나가기", Leave: "나가기",
"I understand, please disable": "기능에 대해 이해했으니 꺼주세요.", "I understand, please disable": "기능에 대해 이해했으니 꺼주세요.",
@ -438,6 +441,7 @@ export default {
Next: "다음", Next: "다음",
"The slug is already taken. Please choose another slug.": "이미 존재하는 주소에요. 다른 주소를 사용해 주세요.", "The slug is already taken. Please choose another slug.": "이미 존재하는 주소에요. 다른 주소를 사용해 주세요.",
"No Proxy": "프록시 없음", "No Proxy": "프록시 없음",
Authentication: "인증",
"HTTP Basic Auth": "HTTP 인증", "HTTP Basic Auth": "HTTP 인증",
"New Status Page": "새로운 상태 페이지", "New Status Page": "새로운 상태 페이지",
"Page Not Found": "페이지를 찾을 수 없어요", "Page Not Found": "페이지를 찾을 수 없어요",

View file

@ -96,6 +96,9 @@ export default {
"Update Password": "Oppdater passord", "Update Password": "Oppdater passord",
"Disable Auth": "Deaktiver autentisering", "Disable Auth": "Deaktiver autentisering",
"Enable Auth": "Aktiver autentisering", "Enable Auth": "Aktiver autentisering",
"disableauth.message1": "Er du sikker på at du vil <strong>deaktiver autentisering</strong>?",
"disableauth.message2": "Dette er for <strong>de som har tredjepartsautorisering</strong> foran Uptime Kuma, for eksempel Cloudflare Access.",
"Please use this option carefully!": "Vennligst vær forsiktig.",
Logout: "Logg ut", Logout: "Logg ut",
Leave: "Forlat", Leave: "Forlat",
"I understand, please disable": "Jeg forstår, vennligst deaktiver", "I understand, please disable": "Jeg forstår, vennligst deaktiver",

View file

@ -90,8 +90,11 @@ export default {
"New Password": "Nieuw wachtwoord", "New Password": "Nieuw wachtwoord",
"Repeat New Password": "Herhaal nieuw wachtwoord", "Repeat New Password": "Herhaal nieuw wachtwoord",
"Update Password": "Vernieuw wachtwoord", "Update Password": "Vernieuw wachtwoord",
"Disable Auth": "Autorisatie uitschakelen", "Disable Auth": "Authenticatie uitschakelen",
"Enable Auth": "Autorisatie inschakelen", "Enable Auth": "Authenticatie inschakelen",
"disableauth.message1": "Weet je zeker dat je <strong>authenticatie wilt uitschakelen</strong>?",
"disableauth.message2": "Er zijn omstandigheden waarbij je <strong>authenticatie door derden wilt implementeren</strong> voor Uptime Kuma, zoals Cloudflare Access, Authelia of andere authenticatiemechanismen.",
"Please use this option carefully!": "Gebruik deze optie zorgvuldig!",
Logout: "Uitloggen", Logout: "Uitloggen",
Leave: "Vertrekken", Leave: "Vertrekken",
"I understand, please disable": "Ik begrijp het, schakel a.u.b. uit", "I understand, please disable": "Ik begrijp het, schakel a.u.b. uit",
@ -351,7 +354,7 @@ export default {
Discard: "Weggooien", Discard: "Weggooien",
Cancel: "Annuleren", Cancel: "Annuleren",
"Powered by": "Mogelijk gemaakt door", "Powered by": "Mogelijk gemaakt door",
shrinkDatabaseDescription: "Trigger database VACUUM voor SQLite. Als de database na 1.10.0 gemaakt is, dan is AUTO_VACUUM al aangezet en deze actie niet nodig.", shrinkDatabaseDescription: "Activeer database VACUUM voor SQLite. Als de database na 1.10.0 aangemaakt is, dan staat AUTO_VACUUM al aan en is deze actie niet nodig.",
serwersms: "SerwerSMS.pl", serwersms: "SerwerSMS.pl",
serwersmsAPIUser: "API Gebruikersnaam (incl. webapi_ prefix)", serwersmsAPIUser: "API Gebruikersnaam (incl. webapi_ prefix)",
serwersmsAPIPassword: "API Wachtwoord", serwersmsAPIPassword: "API Wachtwoord",
@ -386,7 +389,7 @@ export default {
proxyDescription: "Proxies moeten worden toegewezen aan een monitor om te functioneren.", proxyDescription: "Proxies moeten worden toegewezen aan een monitor om te functioneren.",
enableProxyDescription: "Deze proxy heeft geen effect op monitor verzoeken totdat het is geactiveerd. Je kunt tijdelijk de proxy uitschakelen voor alle monitors voor activatie status.", enableProxyDescription: "Deze proxy heeft geen effect op monitor verzoeken totdat het is geactiveerd. Je kunt tijdelijk de proxy uitschakelen voor alle monitors voor activatie status.",
setAsDefaultProxyDescription: "Deze proxy wordt standaard aangezet voor alle nieuwe monitors. Je kunt nog steeds de proxy apart uitschakelen voor elke monitor.", setAsDefaultProxyDescription: "Deze proxy wordt standaard aangezet voor alle nieuwe monitors. Je kunt nog steeds de proxy apart uitschakelen voor elke monitor.",
"Certificate Chain": "Certificaat Chain", "Certificate Chain": "Certificaatketen",
Valid: "Geldig", Valid: "Geldig",
Invalid: "Ongeldig", Invalid: "Ongeldig",
AccessKeyId: "AccessKey ID", AccessKeyId: "AccessKey ID",
@ -407,7 +410,7 @@ export default {
High: "Hoog", High: "Hoog",
Retry: "Opnieuw", Retry: "Opnieuw",
Topic: "Onderwerp", Topic: "Onderwerp",
"WeCom Bot Key": "WeCom Bot Sleutel", "WeCom Bot Key": "WeCom Bot Key",
"Setup Proxy": "Proxy instellen", "Setup Proxy": "Proxy instellen",
"Proxy Protocol": "Proxy Protocol", "Proxy Protocol": "Proxy Protocol",
"Proxy Server": "Proxy Server", "Proxy Server": "Proxy Server",
@ -449,7 +452,6 @@ export default {
"Issuer:": "Uitgever:", "Issuer:": "Uitgever:",
"Fingerprint:": "Vingerafruk:", "Fingerprint:": "Vingerafruk:",
"No status pages": "Geen status pagina's", "No status pages": "Geen status pagina's",
"Domain Name Expiry Notification": "Domein Naam Verloop Notificatie",
Proxy: "Proxy", Proxy: "Proxy",
"Date Created": "Datum Aangemaakt", "Date Created": "Datum Aangemaakt",
onebotHttpAddress: "OneBot HTTP Adres", onebotHttpAddress: "OneBot HTTP Adres",
@ -460,6 +462,70 @@ export default {
onebotSafetyTips: "Voor de veiligheid moet een toegangssleutel worden ingesteld", onebotSafetyTips: "Voor de veiligheid moet een toegangssleutel worden ingesteld",
"PushDeer Key": "PushDeer Key", "PushDeer Key": "PushDeer Key",
"Footer Text": "Footer Tekst", "Footer Text": "Footer Tekst",
"Show Powered By": "Laat 'Mogeljik gemaakt door' zien", "Show Powered By": "Laat \"Mogeljik gemaakt door\" zien",
"Domain Names": "Domein Namen", "Domain Names": "Domein Namen",
"pushoversounds pushover": "Pushover (default)",
"pushoversounds bike": "Bike",
"pushoversounds bugle": "Bugle",
"pushoversounds cashregister": "Cash Register",
"pushoversounds classical": "Classical",
"pushoversounds cosmic": "Cosmic",
"pushoversounds falling": "Falling",
"pushoversounds gamelan": "Gamelan",
"pushoversounds incoming": "Incoming",
"pushoversounds intermission": "Intermission",
"pushoversounds magic": "Magic",
"pushoversounds mechanical": "Mechanical",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Siren",
"pushoversounds spacealarm": "Space Alarm",
"pushoversounds tugboat": "Tug Boat",
"pushoversounds alien": "Alien Alarm (long)",
"pushoversounds climb": "Climb (long)",
"pushoversounds persistent": "Persistent (long)",
"pushoversounds echo": "Pushover Echo (long)",
"pushoversounds updown": "Up Down (long)",
"pushoversounds vibrate": "Vibrate Only",
"pushoversounds none": "None (silent)",
dnsPortDescription: "DNS-serverpoort. Standaard ingesteld op 53. Je kunt de poort op elk moment wijzigen.",
error: "fout",
critical: "kritisch",
wayToGetPagerDutyKey: "Je kunt dit krijgen door naar Service -> Service Directory -> (Selecteer een service) -> Integraties -> Integratie toevoegen te gaan. Hier kunt u zoeken naar \"Events API V2\". Meer informatie {0}",
"Integration Key": "Integration Key",
"Integration URL": "Integration URL",
"Auto resolve or acknowledged": "Automatisch oplossen of bevestigen",
"do nothing": "niets doen",
"auto acknowledged": "automatisch bevestigen",
"auto resolve": "automatisch oplossen",
Authentication: "authenticatie",
signedInDisp: "Aangemeld als {0}",
signedInDispDisabled: "Authenticatie uitgeschakeld.",
"Certificate Expiry Notification": "Melding over verlopen certificaat",
"Recipient Number": "Nummer ontvanger",
"From Name/Number": "Van naam/nummer",
"Leave blank to use a shared sender number.": "Laat leeg om een gedeeld afzendernummer te gebruiken.",
endpoint: "endpoint",
pushyAPIKey: "Secret API Key",
pushyToken: "Device token",
"Show update if available": "Update weergeven indien beschikbaar",
"Also check beta release": "Controleer ook de bètaversies",
"Using a Reverse Proxy?": "Een reverse proxy gebruiken?",
"Check how to config it for WebSocket": "Controleer hoe je het configureert voor een WebSocket",
"Steam Game Server": "Steam gameserver",
"Most likely causes:": "Meest waarschijnlijke oorzaken:",
"The resource is no longer available.": "De paginabron is niet langer beschikbaar.",
"There might be a typing error in the address.": "Er zit een typefout in het de URL.",
"What you can try:": "Wat je kan proberen:",
"Retype the address.": "De URL controleren en/of opnnieuw typen.",
"Go back to the previous page.": "Terug naar de vorige pagina.",
"Coming Soon": "Binnenkort beschikbaar",
wayToGetClickSendSMSToken: "Je kan een API Username en API Key krijgen vanuit {0} .",
"Connection String": "Connection String",
Query: "Query",
settingsCertificateExpiry: "TLS Certificate Expiry",
certificationExpiryDescription: "HTTPS Monitors trigger notification when TLS certificate expires in:",
"ntfy Topic": "ntfy Topic",
Domain: "Domein",
Workstation: "Werkstation",
disableCloudflaredNoAuthMsg: "De \"Geen authenticatie\" modus staat aan, wachtwoord is niet vereist.",
}; };

View file

@ -96,6 +96,9 @@ export default {
"Update Password": "Zaktualizuj hasło", "Update Password": "Zaktualizuj hasło",
"Disable Auth": "Wyłącz autoryzację", "Disable Auth": "Wyłącz autoryzację",
"Enable Auth": "Włącz autoryzację", "Enable Auth": "Włącz autoryzację",
"disableauth.message1": "Czy na pewno chcesz <strong>wyłączyć autoryzację</strong>?",
"disableauth.message2": "Jest przeznaczony dla <strong>kogoś, kto ma autoryzację zewnętrzną</strong> przed Uptime Kuma, taką jak Cloudflare Access.",
"Please use this option carefully!": "Proszę używać ostrożnie.",
Logout: "Wyloguj", Logout: "Wyloguj",
Leave: "Zostaw", Leave: "Zostaw",
"I understand, please disable": "Rozumiem, proszę wyłączyć", "I understand, please disable": "Rozumiem, proszę wyłączyć",
@ -428,6 +431,7 @@ export default {
Next: "Dalej", Next: "Dalej",
"The slug is already taken. Please choose another slug.": "Ten symbol jest już zajęty. Proszę, wybierz inny.", "The slug is already taken. Please choose another slug.": "Ten symbol jest już zajęty. Proszę, wybierz inny.",
"No Proxy": "Bez proxy", "No Proxy": "Bez proxy",
Authentication: "Uwierzytelnianie",
"HTTP Basic Auth": "Podstawowa autoryzacja HTTP", "HTTP Basic Auth": "Podstawowa autoryzacja HTTP",
"New Status Page": "Nowa strona statusu", "New Status Page": "Nowa strona statusu",
"Page Not Found": "Strona nie została znaleziona", "Page Not Found": "Strona nie została znaleziona",

View file

@ -96,6 +96,9 @@ export default {
"Update Password": "Atualizar Senha", "Update Password": "Atualizar Senha",
"Disable Auth": "Desativar Autenticação", "Disable Auth": "Desativar Autenticação",
"Enable Auth": "Ativar Autenticação", "Enable Auth": "Ativar Autenticação",
"disableauth.message1": "Você tem certeza que deseja <strong>desativar a autenticação</strong>?",
"disableauth.message2": "Isso é para <strong>alguém que tem autenticação de terceiros</strong> na frente do 'UpTime Kuma' como o Cloudflare Access.",
"Please use this option carefully!": "Por favor, utilize isso com cautela.",
Logout: "Deslogar", Logout: "Deslogar",
Leave: "Sair", Leave: "Sair",
"I understand, please disable": "Eu entendo, por favor desative.", "I understand, please disable": "Eu entendo, por favor desative.",

View file

@ -84,6 +84,9 @@ export default {
"Update Password": "Обновить пароль", "Update Password": "Обновить пароль",
"Disable Auth": "Отключить авторизацию", "Disable Auth": "Отключить авторизацию",
"Enable Auth": "Включить авторизацию", "Enable Auth": "Включить авторизацию",
"disableauth.message1": "Вы уверены, что хотите <strong>отключить авторизацию</strong>?",
"disableauth.message2": "Это подходит для <strong>тех, у кого стоит другая авторизация</strong> перед открытием Uptime Kuma, например Cloudflare Access.",
"Please use this option carefully!": "Пожалуйста, используйте с осторожностью.",
Logout: "Выйти", Logout: "Выйти",
Leave: "Отмена", Leave: "Отмена",
"I understand, please disable": "Я понимаю, всё равно отключить", "I understand, please disable": "Я понимаю, всё равно отключить",
@ -351,7 +354,8 @@ export default {
"Start or end with a-z 0-9 only": "Начало и окончание имени только на символы: a-z 0-9", "Start or end with a-z 0-9 only": "Начало и окончание имени только на символы: a-z 0-9",
"No consecutive dashes --": "Запрещено использовать тире --", "No consecutive dashes --": "Запрещено использовать тире --",
"HTTP Options": "HTTP Опции", "HTTP Options": "HTTP Опции",
"Basic Auth": "HTTP Авторизация", Authentication: "Аутентификация",
"HTTP Basic Auth": "HTTP Авторизация",
PushByTechulus: "Push by Techulus", PushByTechulus: "Push by Techulus",
clicksendsms: "ClickSend SMS", clicksendsms: "ClickSend SMS",
GoogleChat: "Google Chat (только Google Workspace)", GoogleChat: "Google Chat (только Google Workspace)",

View file

@ -100,6 +100,9 @@ export default {
"Update Password": "Posodobi geslo", "Update Password": "Posodobi geslo",
"Disable Auth": "Onemogoči auth", "Disable Auth": "Onemogoči auth",
"Enable Auth": "Omogoči auth", "Enable Auth": "Omogoči auth",
"disableauth.message1": "Ali ste prepričani, da želite onemogočiti <strong>avtentikacijo</strong>?",
"disableauth.message2": "Namenjen je <strong>nekomu, ki ima pred programom Uptime Kuma vklopljeno zunanje preverjanje pristnosti</strong>, na primer Cloudflare Access.",
"Please use this option carefully!": "Uporabljajte previdno.",
Logout: "Odjava", Logout: "Odjava",
Leave: "Zapusti", Leave: "Zapusti",
"I understand, please disable": "Razumem, prosim onemogočite", "I understand, please disable": "Razumem, prosim onemogočite",
@ -181,8 +184,8 @@ export default {
"Add a monitor": "Dodaj monitor", "Add a monitor": "Dodaj monitor",
"Edit Status Page": "Uredi statusno stran", "Edit Status Page": "Uredi statusno stran",
"Go to Dashboard": "Pojdi na nadzorno ploščo", "Go to Dashboard": "Pojdi na nadzorno ploščo",
"Status Page": "Página de Status", "Status Page": "Statusna stran",
"Status Pages": "Página de Status", "Status Pages": "Statusne strani",
defaultNotificationName: "Moje {notification} Obvestilo ({number})", defaultNotificationName: "Moje {notification} Obvestilo ({number})",
here: "tukaj", here: "tukaj",
Required: "Obvezno", Required: "Obvezno",

View file

@ -84,6 +84,9 @@ export default {
"Update Password": "Izmeni lozinku", "Update Password": "Izmeni lozinku",
"Disable Auth": "Isključi autentifikaciju", "Disable Auth": "Isključi autentifikaciju",
"Enable Auth": "Uključi autentifikaciju", "Enable Auth": "Uključi autentifikaciju",
"disableauth.message1": "Da li ste sigurni da želite da <strong>isključite autentifikaciju</strong>?",
"disableauth.message2": "To je za <strong>one koji imaju dodatu autentifikaciju</strong> ispred Uptime Kuma kao na primer Cloudflare Access.",
"Please use this option carefully!": "Molim Vas koristite ovo sa pažnjom.",
Logout: "Odloguj se", Logout: "Odloguj se",
Leave: "Izađi", Leave: "Izađi",
"I understand, please disable": "Razumem, molim te isključi", "I understand, please disable": "Razumem, molim te isključi",

View file

@ -84,6 +84,9 @@ export default {
"Update Password": "Измени лозинку", "Update Password": "Измени лозинку",
"Disable Auth": "Искључи аутентификацију", "Disable Auth": "Искључи аутентификацију",
"Enable Auth": "Укључи аутентификацију", "Enable Auth": "Укључи аутентификацију",
"disableauth.message1": "Да ли сте сигурни да желите да <strong>искључите аутентификацију</strong>?",
"disableauth.message2": "То је за <strong>оне који имају додату аутентификацију</strong> испред Uptime Kuma као на пример Cloudflare Access.",
"Please use this option carefully!": "Молим Вас користите ово са пажњом.",
Logout: "Одлогуј се", Logout: "Одлогуј се",
Leave: "Изађи", Leave: "Изађи",
"I understand, please disable": "Разумем, молим те искључи", "I understand, please disable": "Разумем, молим те искључи",

View file

@ -101,6 +101,9 @@ export default {
"Update Password": "อัพเดทรหัสผ่าน", "Update Password": "อัพเดทรหัสผ่าน",
"Disable Auth": "ปิดใช้งานการตรวจสอบสิทธิ์", "Disable Auth": "ปิดใช้งานการตรวจสอบสิทธิ์",
"Enable Auth": "เปิดใช้งานการตรวจสอบสิทธิ์", "Enable Auth": "เปิดใช้งานการตรวจสอบสิทธิ์",
"disableauth.message1": "คุณต้องการที่จะ <strong>ปิดใช้งานระบบรับรองความถูกต้องใช่หรือไม่</strong>?",
"disableauth.message2": "ระบบนี้ถูกออกแบบมาเพื่อการใช้งานกับระบบรับรองความถูกต้องของบุคคลที่สามเช่น Cloudflare Access, Authelia หรือวิธีการอื่น ๆ",
"Please use this option carefully!": "โปรดใช้ความระมัดระวังในการเลือกใช้งานระบบนี้ !",
Logout: "ออกจากระบบ", Logout: "ออกจากระบบ",
Leave: "ออก", Leave: "ออก",
"I understand, please disable": "ฉันเข้าใจแล้ว, กรุณาปิดการใช้งาน", "I understand, please disable": "ฉันเข้าใจแล้ว, กรุณาปิดการใช้งาน",

View file

@ -101,6 +101,9 @@ export default {
"Update Password": "Şifreyi Değiştir", "Update Password": "Şifreyi Değiştir",
"Disable Auth": "Şifreli girişi iptal et.", "Disable Auth": "Şifreli girişi iptal et.",
"Enable Auth": "Şifreli girişi aktif et.", "Enable Auth": "Şifreli girişi aktif et.",
"disableauth.message1": "<strong>Şifreli girişi devre dışı bırakmak istediğinizden</strong>emin misiniz?",
"disableauth.message2": "Bu, Uptime Kuma'nın önünde Cloudflare Access gibi <strong>üçüncü taraf yetkilendirmesi olan</strong> kişiler içindir.",
"Please use this option carefully!": "Lütfen dikkatli kullanın.",
Logout: ıkış yap", Logout: ıkış yap",
Leave: "Ayrıl", Leave: "Ayrıl",
"I understand, please disable": "Evet farkındayım, iptal et", "I understand, please disable": "Evet farkındayım, iptal et",

View file

@ -1,5 +1,5 @@
export default { export default {
languageName: "Український", languageName: "Українська",
checkEverySecond: "Перевірка кожні {0} секунд", checkEverySecond: "Перевірка кожні {0} секунд",
retriesDescription: "Максимальна кількість спроб перед позначенням сервісу як недоступного та надсиланням повідомлення", retriesDescription: "Максимальна кількість спроб перед позначенням сервісу як недоступного та надсиланням повідомлення",
ignoreTLSError: "Ігнорувати помилку TLS/SSL для сайтів HTTPS", ignoreTLSError: "Ігнорувати помилку TLS/SSL для сайтів HTTPS",
@ -7,11 +7,11 @@ export default {
maxRedirectDescription: "Максимальна кількість перенаправлень. Поставте 0, щоб вимкнути перенаправлення.", maxRedirectDescription: "Максимальна кількість перенаправлень. Поставте 0, щоб вимкнути перенаправлення.",
acceptedStatusCodesDescription: "Виберіть коди статусів для визначення доступності сервісу.", acceptedStatusCodesDescription: "Виберіть коди статусів для визначення доступності сервісу.",
passwordNotMatchMsg: "Повторення паролю не збігається.", passwordNotMatchMsg: "Повторення паролю не збігається.",
notificationDescription: "Прив'яжіть повідомлення до моніторів.", notificationDescription: "Прив'яжіть сповіщення до моніторів.",
keywordDescription: "Пошук слова в чистому HTML або JSON-відповіді (чутливо до регістру)", keywordDescription: "Пошук слова в чистому HTML або JSON-відповіді (чутливо до регістру)",
pauseDashboardHome: "Пауза", pauseDashboardHome: "Пауза",
deleteMonitorMsg: "Ви дійсно хочете видалити цей монітор?", deleteMonitorMsg: "Ви дійсно хочете видалити цей монітор?",
deleteNotificationMsg: "Ви дійсно хочете видалити це повідомлення для всіх моніторів?", deleteNotificationMsg: "Ви дійсно хочете видалити це сповіщення для всіх моніторів?",
resolverserverDescription: "Cloudflare є сервером за замовчуванням. Ви завжди можете змінити цей сервер.", resolverserverDescription: "Cloudflare є сервером за замовчуванням. Ви завжди можете змінити цей сервер.",
rrtypeDescription: "Виберіть тип ресурсного запису, який ви хочете відстежувати", rrtypeDescription: "Виберіть тип ресурсного запису, який ви хочете відстежувати",
pauseMonitorMsg: "Ви дійсно хочете поставити на паузу?", pauseMonitorMsg: "Ви дійсно хочете поставити на паузу?",
@ -54,7 +54,7 @@ export default {
Keyword: "Ключове слово", Keyword: "Ключове слово",
"Friendly Name": "Ім'я", "Friendly Name": "Ім'я",
URL: "URL", URL: "URL",
Hostname: "Ім'я хоста", Hostname: "Адреса хоста",
Port: "Порт", Port: "Порт",
"Heartbeat Interval": "Частота опитування", "Heartbeat Interval": "Частота опитування",
Retries: "Спроб", Retries: "Спроб",
@ -63,7 +63,7 @@ export default {
"Max. Redirects": "Макс. кількість перенаправлень", "Max. Redirects": "Макс. кількість перенаправлень",
"Accepted Status Codes": "Припустимі коди статусу", "Accepted Status Codes": "Припустимі коди статусу",
Save: "Зберегти", Save: "Зберегти",
Notifications: "Повідомлення", Notifications: "Сповіщення",
"Not available, please setup.": "Доступних сповіщень немає, необхідно створити.", "Not available, please setup.": "Доступних сповіщень немає, необхідно створити.",
"Setup Notification": "Створити сповіщення", "Setup Notification": "Створити сповіщення",
Light: "Світла", Light: "Світла",
@ -84,6 +84,9 @@ export default {
"Update Password": "Оновити пароль", "Update Password": "Оновити пароль",
"Disable Auth": "Вимкнути авторизацію", "Disable Auth": "Вимкнути авторизацію",
"Enable Auth": "Увімкнути авторизацію", "Enable Auth": "Увімкнути авторизацію",
"disableauth.message1": "Ви впевнені, що бажаєте <strong>вимкнути авторизацію</strong>?",
"disableauth.message2": "Це підходить для <strong>тих, у кого встановлена інша авторизація</strong> пееред відкриттям Uptime Kuma, наприклад Cloudflare Access.",
"Please use this option carefully!": "Будь ласка, використовуйте з обережністю.",
Logout: "Вийти", Logout: "Вийти",
Leave: "Відміна", Leave: "Відміна",
"I understand, please disable": "Я розумію, все одно відключити", "I understand, please disable": "Я розумію, все одно відключити",
@ -97,7 +100,7 @@ export default {
"No Monitors, please": "Моніторів немає, будь ласка", "No Monitors, please": "Моніторів немає, будь ласка",
"No Monitors": "Монітори відсутні", "No Monitors": "Монітори відсутні",
"add one": "створіть новий", "add one": "створіть новий",
"Notification Type": "Тип повідомлення", "Notification Type": "Тип сповіщення",
Email: "Пошта", Email: "Пошта",
Test: "Перевірка", Test: "Перевірка",
"Certificate Info": "Інформація про сертифікат", "Certificate Info": "Інформація про сертифікат",
@ -116,7 +119,7 @@ export default {
Events: "Події", Events: "Події",
Heartbeats: "Опитування", Heartbeats: "Опитування",
"Auto Get": "Авто-отримання", "Auto Get": "Авто-отримання",
enableDefaultNotificationDescription: "Для кожного нового монітора це повідомлення буде включено за замовчуванням. Ви все ще можете відключити повідомлення в кожному моніторі окремо.", enableDefaultNotificationDescription: "Для кожного нового монітора це сповіщення буде включено за замовчуванням. Ви все ще можете відключити сповіщення в кожному моніторі окремо.",
"Default enabled": "Використовувати за промовчанням", "Default enabled": "Використовувати за промовчанням",
"Also apply to existing monitors": "Застосувати до існуючих моніторів", "Also apply to existing monitors": "Застосувати до існуючих моніторів",
Export: "Експорт", Export: "Експорт",
@ -167,7 +170,7 @@ export default {
Purple: "Пурпурний", Purple: "Пурпурний",
Pink: "Рожевий", Pink: "Рожевий",
"Search...": "Пошук...", "Search...": "Пошук...",
"Avg. Ping": "Середнє значення пінгу", "Avg. Ping": "Середній пінг",
"Avg. Response": "Середній час відповіді", "Avg. Response": "Середній час відповіді",
"Entry Page": "Головна сторінка", "Entry Page": "Головна сторінка",
statusPageNothing: "Тут порожньо. Додайте групу або монітор.", statusPageNothing: "Тут порожньо. Додайте групу або монітор.",
@ -207,7 +210,7 @@ export default {
"Push URL": "URL пуша", "Push URL": "URL пуша",
needPushEvery: "До цієї URL необхідно звертатися кожні {0} секунд", needPushEvery: "До цієї URL необхідно звертатися кожні {0} секунд",
pushOptionalParams: "Опціональні параметри: {0}", pushOptionalParams: "Опціональні параметри: {0}",
defaultNotificationName: "Моє повідомлення {notification} ({number})", defaultNotificationName: "Моє сповіщення {notification} ({number})",
here: "тут", here: "тут",
Required: "Потрібно", Required: "Потрібно",
"Bot Token": "Токен бота", "Bot Token": "Токен бота",
@ -254,7 +257,7 @@ export default {
"User Key": "Ключ користувача", "User Key": "Ключ користувача",
Device: "Пристрій", Device: "Пристрій",
"Message Title": "Заголовок повідомлення", "Message Title": "Заголовок повідомлення",
"Notification Sound": "Звук повідомлення", "Notification Sound": "Звук сповіщення",
"More info on:": "Більше інформації: {0}", "More info on:": "Більше інформації: {0}",
pushoverDesc1: "Екстренний пріоритет (2) має таймуут повтору за замовчуванням 30 секунд і закінчується через 1 годину.", pushoverDesc1: "Екстренний пріоритет (2) має таймуут повтору за замовчуванням 30 секунд і закінчується через 1 годину.",
pushoverDesc2: "Якщо ви бажаєте надсилати повідомлення різним пристроям, необхідно заповнити поле Пристрій.", pushoverDesc2: "Якщо ви бажаєте надсилати повідомлення різним пристроям, необхідно заповнити поле Пристрій.",
@ -350,7 +353,8 @@ export default {
"Start or end with a-z 0-9 only": "Початок та закінчення імені лише на символи: a-z 0-9", "Start or end with a-z 0-9 only": "Початок та закінчення імені лише на символи: a-z 0-9",
"No consecutive dashes --": "Заборонено використовувати тире --", "No consecutive dashes --": "Заборонено використовувати тире --",
"HTTP Options": "HTTP Опції", "HTTP Options": "HTTP Опції",
"Basic Auth": "HTTP Авторизація", Authentication: "Аутентифікація",
"HTTP Basic Auth": "Базова HTTP",
PushByTechulus: "Push by Techulus", PushByTechulus: "Push by Techulus",
clicksendsms: "ClickSend SMS", clicksendsms: "ClickSend SMS",
GoogleChat: "Google Chat (тільки Google Workspace)", GoogleChat: "Google Chat (тільки Google Workspace)",
@ -388,4 +392,139 @@ export default {
alertaAlertState: "Стан алерту", alertaAlertState: "Стан алерту",
alertaRecoverState: "Стан відновлення", alertaRecoverState: "Стан відновлення",
deleteStatusPageMsg: "Дійсно хочете видалити цю сторінку статусів?", deleteStatusPageMsg: "Дійсно хочете видалити цю сторінку статусів?",
Proxies: "Проксі",
default: "За замовчуванням",
enabled: "Активно",
setAsDefault: "Встановити за замовчуванням",
deleteProxyMsg: "Ви впевнені, що хочете видалити цей проксі для всіх моніторів?",
proxyDescription: "Щоб функціонувати, монітору потрібно призначити проксі.",
enableProxyDescription: "Цей проксі не впливатиме на запити моніторингу, доки його не буде активовано. Ви можете контролювати тимчасове відключення проксі з усіх моніторів за статусом активації.",
setAsDefaultProxyDescription: "Цей проксі буде ввімкнено за умовчанням для нових моніторів. Ви все одно можете вимкнути проксі окремо для кожного монітора.",
Invalid: "Недійсний",
AccessKeyId: "AccessKey ID",
SecretAccessKey: "AccessKey Secret",
PhoneNumbers: "PhoneNumbers",
TemplateCode: "TemplateCode",
SignName: "SignName",
"Sms template must contain parameters: ": "Шаблон смс повинен містити параметри: ",
"Bark Endpoint": "Bark Endpoint",
WebHookUrl: "WebHookUrl",
SecretKey: "SecretKey",
"For safety, must use secret key": "Для безпеки необхідно використовувати секретний ключ",
"Device Token": "Токен пристрою",
Platform: "Платформа",
iOS: "iOS",
Android: "Android",
Huawei: "Huawei",
High: "Високий",
Retry: "Повтор",
Topic: "Тема",
"WeCom Bot Key": "WeCom Bot ключ",
"Setup Proxy": "Налаштувати проксі",
"Proxy Protocol": "Протокол проксі",
"Proxy Server": "Проксі-сервер",
"Proxy server has authentication": "Проксі-сервер має аутентифікацію",
User: "Користувач",
Installed: "Встановлено",
"Not installed": "Не встановлено",
Running: "Запущено",
"Not running": "Не запущено",
"Remove Token": "Видалити токен",
Start: "Запустити",
Stop: "Зупинити",
"Uptime Kuma": "Uptime Kuma",
Slug: "Slug",
"Accept characters:": "Прийняти символи:",
startOrEndWithOnly: "Починається або закінчується лише {0}",
"No consecutive dashes": "Немає послідовних тире",
"The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.",
"No Proxy": "Без проксі",
"Page Not Found": "Сторінку не знайдено",
"Reverse Proxy": "Реверсивний проксі",
wayToGetCloudflaredURL: "(Завантажити Cloudflare з {0})",
cloudflareWebsite: "Веб-сайт Cloudflare",
"Message:": "Повідомлення:",
"Don't know how to get the token? Please read the guide:": "Не знаєте, як отримати токен? Прочитайте посібник:",
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Поточне з’єднання може бути втрачено, якщо ви зараз під’єднуєтеся через Cloudflare Tunnel. Ви дійсно хочете зробити це? Для підтвердження введіть поточний пароль.",
"Other Software": "Інше програмне забезпечення",
"For example: nginx, Apache and Traefik.": "Наприклад: nginx, Apache and Traefik.",
"Please read": "Будь ласка, прочитайте",
"Subject:": "Тема:",
"Valid To:": "Дійсний до:",
"Days Remaining:": "Залишилось днів:",
"Issuer:": "Емітент:",
"Fingerprint:": "Відбиток:",
"No status pages": "Немає сторінок статусу",
"Domain Name Expiry Notification": "Сповіщення про закінчення терміну дії доменного імені",
Proxy: "Проксі",
"Date Created": "Дата створення",
onebotHttpAddress: "OneBot адреса HTTP",
onebotMessageType: "OneBot тип повідомлення",
onebotGroupMessage: "Група",
onebotPrivateMessage: "Приватне",
onebotUserOrGroupId: "Група/Користувач ID",
onebotSafetyTips: "Для безпеки необхідно встановити маркер доступу",
"PushDeer Key": "PushDeer ключ",
"Footer Text": "Текст нижнього колонтитула",
"Show Powered By": "Показувати платформу",
"Domain Names": "Доменні імена",
signedInDisp: "Ви ввійшли як {0}",
signedInDispDisabled: "Авторизація вимкнена.",
"Certificate Expiry Notification": "Сповіщення про закінчення терміну дії сертифіката",
"API Username": "Користувач API",
"API Key": "Ключ API",
"Recipient Number": "Номер одержувача",
"From Name/Number": "Від Ім'я/Номер",
"Leave blank to use a shared sender number.": "Залиште поле порожнім, щоб використовувати спільний номер відправника.",
"Octopush API Version": "Octopush API версія",
"Legacy Octopush-DM": "Legacy Octopush-DM",
"endpoint": "кінцева точка",
octopushAPIKey: "\"Ключ API\" з облікових даних HTTP API в панелі керування",
octopushLogin: "\"Ім'я користувача\" з облікових даних HTTP API на панелі керування",
promosmsLogin: "API Логін",
promosmsPassword: "API Пароль",
"pushoversounds pushover": "Pushover (по замовчуванню)",
"pushoversounds bike": "Bike",
"pushoversounds bugle": "Bugle",
"pushoversounds cashregister": "Cash Register",
"pushoversounds classical": "Classical",
"pushoversounds cosmic": "Cosmic",
"pushoversounds falling": "Falling",
"pushoversounds gamelan": "Gamelan",
"pushoversounds incoming": "Incoming",
"pushoversounds intermission": "Intermission",
"pushoversounds magic": "Magic",
"pushoversounds mechanical": "Mechanical",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Siren",
"pushoversounds spacealarm": "Space Alarm",
"pushoversounds tugboat": "Tug Boat",
"pushoversounds alien": "Alien Alarm (long)",
"pushoversounds climb": "Climb (long)",
"pushoversounds persistent": "Persistent (long)",
"pushoversounds echo": "Pushover Echo (long)",
"pushoversounds updown": "Up Down (long)",
"pushoversounds vibrate": "Vibrate Only",
"pushoversounds none": "None (silent)",
pushyAPIKey: "Секретний ключ API",
pushyToken: "Токен пристрою",
"Using a Reverse Proxy?": "Використовувати зворотній проксі?",
"Check how to config it for WebSocket": "Перевірте, як налаштувати його для WebSocket",
"Steam Game Server": "Ігровий сервер Steam",
"Most likely causes:": "Найімовірніші причини:",
"The resource is no longer available.": "Ресурс більше не доступний.",
"There might be a typing error in the address.": "Можливо, в адресі є помилка.",
"What you can try:": "Що ви можете спробувати:",
"Retype the address.": "Повторно введіть адресу.",
"Go back to the previous page.": "Повернутися на попередню сторінку.",
"Coming Soon": "Незабаром",
wayToGetClickSendSMSToken: "Ви можете отримати ім’я користувача API та ключ API з {0} .",
"Connection String": "Рядок підключення",
"Query": "Запит",
settingsCertificateExpiry: "Закінчення терміну дії сертифіката TLS",
certificationExpiryDescription: "Запуск сповіщення для HTTPS моніторів коли до закінчення терміну дії TLS сертифіката:",
"ntfy Topic": "ntfy Тема",
"Domain": "Домен",
"Workstation": "Робоча станція",
disableCloudflaredNoAuthMsg: "Ви перебуваєте в режимі без авторизації, пароль не потрібен.",
}; };

View file

@ -100,6 +100,9 @@ export default {
"Update Password": "Cập nhật mật khẩu", "Update Password": "Cập nhật mật khẩu",
"Disable Auth": "Tắt xác minh", "Disable Auth": "Tắt xác minh",
"Enable Auth": "Bật xác minh", "Enable Auth": "Bật xác minh",
"disableauth.message1": "Bạn có muốn <strong>TẮT XÁC THỰC</strong> không?",
"disableauth.message2": "Điều này rất nguy hiểm<strong>BẤT KỲ AI</strong> cũng có thể truy cập và cướp quyền điều khiển.",
"Please use this option carefully!": "Vui lòng <strong>cẩn thận</strong>.",
Logout: "Đăng xuất", Logout: "Đăng xuất",
Leave: "Rời", Leave: "Rời",
"I understand, please disable": "Tôi hiểu, làm ơn hãy tắt!", "I understand, please disable": "Tôi hiểu, làm ơn hãy tắt!",

View file

@ -101,6 +101,9 @@ export default {
"Update Password": "更新密码", "Update Password": "更新密码",
"Disable Auth": "禁用身份验证", "Disable Auth": "禁用身份验证",
"Enable Auth": "启用身份验证", "Enable Auth": "启用身份验证",
"disableauth.message1": "是否确定 <strong>取消登录验证</strong>",
"disableauth.message2": "这是为 <strong>有第三方认证</strong> 的用户提供的功能,如 Cloudflare Access",
"Please use this option carefully!": "请谨慎使用!",
Logout: "退出", Logout: "退出",
Leave: "离开", Leave: "离开",
"I understand, please disable": "我已了解,继续禁用", "I understand, please disable": "我已了解,继续禁用",
@ -436,6 +439,7 @@ export default {
Next: "下一步", Next: "下一步",
"The slug is already taken. Please choose another slug.": "该路径已被使用。请选择其他路径。", "The slug is already taken. Please choose another slug.": "该路径已被使用。请选择其他路径。",
"No Proxy": "无代理", "No Proxy": "无代理",
Authentication: "验证",
"HTTP Basic Auth": "HTTP 基础身份验证", "HTTP Basic Auth": "HTTP 基础身份验证",
"New Status Page": "新的状态页", "New Status Page": "新的状态页",
"Page Not Found": "未找到该页面", "Page Not Found": "未找到该页面",
@ -522,11 +526,18 @@ export default {
dnsPortDescription: "DNS 服务器端口,默认为 53你可以在任何时候更改此端口.", dnsPortDescription: "DNS 服务器端口,默认为 53你可以在任何时候更改此端口.",
error: "错误", error: "错误",
critical: "关键", critical: "关键",
wayToGetPagerDutyKey: "你可以在 Service -> Service Directory -> (Select a service) -> Integrations -> Add integration 页面中搜索 \"Events API V2\" 以获取此 Integration Key更多信息请参见 {0}", wayToGetPagerDutyKey: "你可以在 Service -> Service Directory -> (选择一个 Service) -> Integrations -> Add integration 页面中搜索 \"Events API V2\" 以获取此 Integration Key更多信息请参见 {0}",
"Integration Key": "Integration Key", "Integration Key": "Integration Key",
"Integration URL": "Integration URL", "Integration URL": "Integration URL",
"Auto resolve or acknowledged": "自动标记为已解决或已读", "Auto resolve or acknowledged": "自动标记为已解决或已读",
"do nothing": "不做任何操作", "do nothing": "不做任何操作",
"auto acknowledged": "自动标记为已读", "auto acknowledged": "自动标记为已读",
"auto resolve": "自动标记为已解决", "auto resolve": "自动标记为已解决",
"Connection String": "连接字符串",
Query: "查询语句",
settingsCertificateExpiry: "TLS 证书过期通知",
certificationExpiryDescription: "HTTPS 监控项发现被监控目标的 TLS 证书剩余有效期少于以下天数时将发出通知:",
"ntfy Topic": "ntfy 主题",
"Domain": "域名",
"Workstation": "工作站",
}; };

View file

@ -77,6 +77,9 @@ export default {
"Update Password": "更新密碼", "Update Password": "更新密碼",
"Disable Auth": "取消登入認証", "Disable Auth": "取消登入認証",
"Enable Auth": "開啟登入認証", "Enable Auth": "開啟登入認証",
"disableauth.message1": "你是否確認<strong>取消登入認証</strong>",
"disableauth.message2": "這個功能是設計給已有<strong>第三方認証</strong>的用家,例如 Cloudflare Access。",
"Please use this option carefully!": "請小心使用。",
Logout: "登出", Logout: "登出",
notificationDescription: "新增後,你需要在監測器裡啟用。", notificationDescription: "新增後,你需要在監測器裡啟用。",
Leave: "離開", Leave: "離開",

View file

@ -13,6 +13,7 @@ export default {
pauseDashboardHome: "暫停", pauseDashboardHome: "暫停",
deleteMonitorMsg: "您確定要刪除此監測器嗎?", deleteMonitorMsg: "您確定要刪除此監測器嗎?",
deleteNotificationMsg: "您確定要為所有監測器刪除此通知嗎?", deleteNotificationMsg: "您確定要為所有監測器刪除此通知嗎?",
dnsPortDescription: "DNS 伺服器連接埠。預設為 53。您可以隨時變更連接埠。",
resolverserverDescription: "Cloudflare 為預設伺服器。您可以隨時更換解析伺服器。", resolverserverDescription: "Cloudflare 為預設伺服器。您可以隨時更換解析伺服器。",
rrtypeDescription: "選擇您想要監測的資源記錄類型", rrtypeDescription: "選擇您想要監測的資源記錄類型",
pauseMonitorMsg: "您確定要暫停嗎?", pauseMonitorMsg: "您確定要暫停嗎?",
@ -100,6 +101,9 @@ export default {
"Update Password": "更新密碼", "Update Password": "更新密碼",
"Disable Auth": "停用驗證", "Disable Auth": "停用驗證",
"Enable Auth": "啟用驗證", "Enable Auth": "啟用驗證",
"disableauth.message1": ">你是否要<strong>取消登入驗證</strong>",
"disableauth.message2": "此功能是設計給已有<strong>第三方認證</strong>的使用者,例如 Cloudflare Access。",
"Please use this option carefully!": "請謹慎使用。",
Logout: "登出", Logout: "登出",
Leave: "離開", Leave: "離開",
"I understand, please disable": "我了解了,請停用", "I understand, please disable": "我了解了,請停用",
@ -329,6 +333,8 @@ export default {
info: "資訊", info: "資訊",
warning: "警告", warning: "警告",
danger: "危險", danger: "危險",
error: "錯誤",
critical: "嚴重",
primary: "主要", primary: "主要",
light: "淺色", light: "淺色",
dark: "暗色", dark: "暗色",
@ -369,6 +375,13 @@ export default {
smtpDkimHashAlgo: "雜湊演算法 (選填)", smtpDkimHashAlgo: "雜湊演算法 (選填)",
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)", smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
smtpDkimskipFields: "不簽署的郵件標頭 (選填)", smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
wayToGetPagerDutyKey: "您可以前往服務 -> 服務目錄 -> (選取服務) -> 整合 -> 新增整合以取得。您可以搜尋 \"Events API V2\"。詳細資訊 {0}",
"Integration Key": "整合金鑰",
"Integration URL": "整合網址",
"Auto resolve or acknowledged": "自動解決或認可",
"do nothing": "不進行任何操作",
"auto acknowledged": "自動認可",
"auto resolve": "自動解決",
gorush: "Gorush", gorush: "Gorush",
alerta: "Alerta", alerta: "Alerta",
alertaApiEndpoint: "API 端點", alertaApiEndpoint: "API 端點",
@ -428,6 +441,7 @@ export default {
Next: "下一步", Next: "下一步",
"The slug is already taken. Please choose another slug.": "此 slug 已被使用。請選擇其他 slug。", "The slug is already taken. Please choose another slug.": "此 slug 已被使用。請選擇其他 slug。",
"No Proxy": "無 Proxy", "No Proxy": "無 Proxy",
Authentication: "驗證",
"HTTP Basic Auth": "HTTP 基本驗證", "HTTP Basic Auth": "HTTP 基本驗證",
"New Status Page": "新狀態頁", "New Status Page": "新狀態頁",
"Page Not Found": "找不到頁面", "Page Not Found": "找不到頁面",
@ -461,4 +475,65 @@ export default {
"Footer Text": "頁尾文字", "Footer Text": "頁尾文字",
"Show Powered By": "顯示技術支援文字", "Show Powered By": "顯示技術支援文字",
"Domain Names": "網域名稱", "Domain Names": "網域名稱",
signedInDisp: "以 {0} 身分登入",
signedInDispDisabled: "驗證已停用。",
"Certificate Expiry Notification": "憑證到期通知",
"API Username": "API 使用者名稱",
"API Key": "API 金鑰",
"Recipient Number": "收件者號碼",
"From Name/Number": "來自名字/號碼",
"Leave blank to use a shared sender number.": "留空以使用共享寄件人號碼。",
"Octopush API Version": "Octopush API 版本",
"Legacy Octopush-DM": "舊版 Octopush-DM",
"endpoint": "端",
octopushAPIKey: "\"API key\" from HTTP API credentials in control panel",
octopushLogin: "\"Login\" from HTTP API credentials in control panel",
promosmsLogin: "API 登入名稱",
promosmsPassword: "API 密碼",
"pushoversounds pushover": "Pushover (預設)",
"pushoversounds bike": "車鈴",
"pushoversounds bugle": "號角",
"pushoversounds cashregister": "收銀機",
"pushoversounds classical": "古典",
"pushoversounds cosmic": "宇宙",
"pushoversounds falling": "下落",
"pushoversounds gamelan": "甘美朗",
"pushoversounds incoming": "來電",
"pushoversounds intermission": "中場休息",
"pushoversounds magic": "魔法",
"pushoversounds mechanical": "機械",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Siren",
"pushoversounds spacealarm": "Space Alarm",
"pushoversounds tugboat": "汽笛",
"pushoversounds alien": "外星鬧鐘 (長)",
"pushoversounds climb": "爬升 (長)",
"pushoversounds persistent": "持續 (長)",
"pushoversounds echo": "Pushover 回音 (長)",
"pushoversounds updown": "上下 (長)",
"pushoversounds vibrate": "僅震動",
"pushoversounds none": "無 (靜音)",
pushyAPIKey: "API 密鑰",
pushyToken: "裝置權杖",
"Show update if available": "顯示可用更新",
"Also check beta release": "檢查 Beta 版",
"Using a Reverse Proxy?": "正在使用反向代理?",
"Check how to config it for WebSocket": "查看如何為 WebSocket 設定",
"Steam Game Server": "Steam 遊戲伺服器",
"Most likely causes:": "可能原因:",
"The resource is no longer available.": "資源已不可用。",
"There might be a typing error in the address.": "網址可能有誤。",
"What you can try:": "您可以嘗試:",
"Retype the address.": "重新輸入網址。",
"Go back to the previous page.": "返回上一頁。",
"Coming Soon": "即將推出",
wayToGetClickSendSMSToken: "您可以從 {0} 取得 API 使用者名稱和金鑰。",
"Connection String": "連線字串",
"Query": "查詢",
settingsCertificateExpiry: "TLS 憑證到期",
certificationExpiryDescription: "TLS 將於 X 天後到期時觸發 HTTPS 監測器通知:",
"ntfy Topic": "ntfy 主題",
"Domain": "網域",
"Workstation": "工作站",
disableCloudflaredNoAuthMsg: "您處於無驗證模式。無須輸入密碼。",
}; };

View file

@ -18,14 +18,31 @@ export default {
}, },
methods: { methods: {
/**
* Return a given value in the format YYYY-MM-DD HH:mm:ss
* @param {any} value Value to format as date time
* @returns {string}
*/
datetime(value) { datetime(value) {
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss"); return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss");
}, },
/**
* Return a given value in the format YYYY-MM-DD
* @param {any} value Value to format as date
* @returns {string}
*/
date(value) { date(value) {
return this.datetimeFormat(value, "YYYY-MM-DD"); return this.datetimeFormat(value, "YYYY-MM-DD");
}, },
/**
* Return a given value in the format HH:mm or if second is set
* to true, HH:mm:ss
* @param {any} value Value to format
* @param {boolean} second Should seconds be included?
* @returns {string}
*/
time(value, second = true) { time(value, second = true) {
let secondString; let secondString;
if (second) { if (second) {
@ -36,6 +53,12 @@ export default {
return this.datetimeFormat(value, "HH:mm" + secondString); return this.datetimeFormat(value, "HH:mm" + secondString);
}, },
/**
* Return a value in a custom format
* @param {any} value Value to format
* @param {any} format Format to return value in
* @returns {string}
*/
datetimeFormat(value, format) { datetimeFormat(value, format) {
if (value !== undefined && value !== "") { if (value !== undefined && value !== "") {
return dayjs.utc(value).tz(this.timezone).format(format); return dayjs.utc(value).tz(this.timezone).format(format);

View file

@ -22,6 +22,7 @@ export default {
}, },
methods: { methods: {
/** Change the application language */
async changeLang(lang) { async changeLang(lang) {
let message = (await langModules["../languages/" + lang + ".js"]()).default; let message = (await langModules["../languages/" + lang + ".js"]()).default;
this.$i18n.setLocaleMessage(lang, message); this.$i18n.setLocaleMessage(lang, message);

View file

@ -12,11 +12,13 @@ export default {
}, },
methods: { methods: {
/** Handle screen resize */
onResize() { onResize() {
this.windowWidth = window.innerWidth; this.windowWidth = window.innerWidth;
this.updateBody(); this.updateBody();
}, },
/** Add css-class "mobile" to body if needed */
updateBody() { updateBody() {
if (this.isMobile) { if (this.isMobile) {
document.body.classList.add("mobile"); document.body.classList.add("mobile");

View file

@ -63,6 +63,12 @@ export default {
methods: { methods: {
/**
* Initialize connection to socket server
* @param {boolean} [bypass = false] Should the check for if we
* are on a status page be bypassed?
* @returns {(void|null)}
*/
initSocketIO(bypass = false) { initSocketIO(bypass = false) {
// No need to re-init // No need to re-init
if (this.socket.initedSocketIO) { if (this.socket.initedSocketIO) {
@ -263,10 +269,18 @@ export default {
socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res); socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res);
}, },
/**
* The storage currently in use
* @returns {Storage}
*/
storage() { storage() {
return (this.remember) ? localStorage : sessionStorage; return (this.remember) ? localStorage : sessionStorage;
}, },
/**
* Get payload of JWT cookie
* @returns {(Object|undefined)}
*/
getJWTPayload() { getJWTPayload() {
const jwtToken = this.$root.storage().token; const jwtToken = this.$root.storage().token;
@ -276,10 +290,18 @@ export default {
return undefined; return undefined;
}, },
/**
* Get current socket
* @returns {Socket}
*/
getSocket() { getSocket() {
return socket; return socket;
}, },
/**
* Show success or error toast dependant on response status code
* @param {Object} res Response object
*/
toastRes(res) { toastRes(res) {
if (res.ok) { if (res.ok) {
toast.success(res.msg); toast.success(res.msg);
@ -288,14 +310,35 @@ export default {
} }
}, },
/**
* Show a success toast
* @param {string} msg Message to show
*/
toastSuccess(msg) { toastSuccess(msg) {
toast.success(msg); toast.success(msg);
}, },
/**
* Show an error toast
* @param {string} msg Message to show
*/
toastError(msg) { toastError(msg) {
toast.error(msg); toast.error(msg);
}, },
/**
* Callback for login
* @callback loginCB
* @param {Object} res Response object
*/
/**
* Send request to log user in
* @param {string} username Username to log in with
* @param {string} password Password to log in with
* @param {string} token User token
* @param {loginCB} callback Callback to call with result
*/
login(username, password, token, callback) { login(username, password, token, callback) {
socket.emit("login", { socket.emit("login", {
username, username,
@ -320,6 +363,10 @@ export default {
}); });
}, },
/**
* Log in using a token
* @param {string} token Token to log in with
*/
loginByToken(token) { loginByToken(token) {
socket.emit("loginByToken", token, (res) => { socket.emit("loginByToken", token, (res) => {
this.allowLoginDialog = true; this.allowLoginDialog = true;
@ -333,6 +380,7 @@ export default {
}); });
}, },
/** Log out of the web application */
logout() { logout() {
socket.emit("logout", () => { }); socket.emit("logout", () => { });
this.storage().removeItem("token"); this.storage().removeItem("token");
@ -342,26 +390,54 @@ export default {
this.clearData(); this.clearData();
}, },
/**
* Callback for general socket requests
* @callback socketCB
* @param {Object} res Result of operation
*/
/** Prepare 2FA configuration */
prepare2FA(callback) { prepare2FA(callback) {
socket.emit("prepare2FA", callback); socket.emit("prepare2FA", callback);
}, },
/**
* Save the current 2FA configuration
* @param {any} secret Unused
* @param {socketCB} callback
*/
save2FA(secret, callback) { save2FA(secret, callback) {
socket.emit("save2FA", callback); socket.emit("save2FA", callback);
}, },
/**
* Disable 2FA for this user
* @param {socketCB} callback
*/
disable2FA(callback) { disable2FA(callback) {
socket.emit("disable2FA", callback); socket.emit("disable2FA", callback);
}, },
/**
* Verify the provided 2FA token
* @param {string} token Token to verify
* @param {socketCB} callback
*/
verifyToken(token, callback) { verifyToken(token, callback) {
socket.emit("verifyToken", token, callback); socket.emit("verifyToken", token, callback);
}, },
/**
* Get current 2FA status
* @param {socketCB} callback
*/
twoFAStatus(callback) { twoFAStatus(callback) {
socket.emit("twoFAStatus", callback); socket.emit("twoFAStatus", callback);
}, },
/**
* Get list of monitors
* @param {socketCB} callback
*/
getMonitorList(callback) { getMonitorList(callback) {
if (! callback) { if (! callback) {
callback = () => { }; callback = () => { };
@ -369,36 +445,74 @@ export default {
socket.emit("getMonitorList", callback); socket.emit("getMonitorList", callback);
}, },
/**
* Add a monitor
* @param {Object} monitor Object representing monitor to add
* @param {socketCB} callback
*/
add(monitor, callback) { add(monitor, callback) {
socket.emit("add", monitor, callback); socket.emit("add", monitor, callback);
}, },
/**
* Delete monitor by ID
* @param {number} monitorID ID of monitor to delete
* @param {socketCB} callback
*/
deleteMonitor(monitorID, callback) { deleteMonitor(monitorID, callback) {
socket.emit("deleteMonitor", monitorID, callback); socket.emit("deleteMonitor", monitorID, callback);
}, },
/** Clear the hearbeat list */
clearData() { clearData() {
console.log("reset heartbeat list"); console.log("reset heartbeat list");
this.heartbeatList = {}; this.heartbeatList = {};
this.importantHeartbeatList = {}; this.importantHeartbeatList = {};
}, },
/**
* Upload the provided backup
* @param {string} uploadedJSON JSON to upload
* @param {string} importHandle Type of import. If set to
* most data in database will be replaced
* @param {socketCB} callback
*/
uploadBackup(uploadedJSON, importHandle, callback) { uploadBackup(uploadedJSON, importHandle, callback) {
socket.emit("uploadBackup", uploadedJSON, importHandle, callback); socket.emit("uploadBackup", uploadedJSON, importHandle, callback);
}, },
/**
* Clear events for a specified monitor
* @param {number} monitorID ID of monitor to clear
* @param {socketCB} callback
*/
clearEvents(monitorID, callback) { clearEvents(monitorID, callback) {
socket.emit("clearEvents", monitorID, callback); socket.emit("clearEvents", monitorID, callback);
}, },
/**
* Clear the heartbeats of a specified monitor
* @param {number} monitorID Id of monitor to clear
* @param {socketCB} callback
*/
clearHeartbeats(monitorID, callback) { clearHeartbeats(monitorID, callback) {
socket.emit("clearHeartbeats", monitorID, callback); socket.emit("clearHeartbeats", monitorID, callback);
}, },
/**
* Clear all statistics
* @param {socketCB} callback
*/
clearStatistics(callback) { clearStatistics(callback) {
socket.emit("clearStatistics", callback); socket.emit("clearStatistics", callback);
}, },
/**
* Get monitor beats for a specific monitor in a time range
* @param {number} monitorID ID of monitor to fetch
* @param {number} period Time in hours from now
* @param {socketCB} callback
*/
getMonitorBeats(monitorID, period, callback) { getMonitorBeats(monitorID, period, callback) {
socket.emit("getMonitorBeats", monitorID, period, callback); socket.emit("getMonitorBeats", monitorID, period, callback);
} }
@ -492,6 +606,28 @@ export default {
return result; return result;
}, },
/**
* Frontend Version
* It should be compiled to a static value while building the frontend.
* Please see ./config/vite.config.js, it is defined via vite.js
* @returns {string}
*/
frontendVersion() {
// eslint-disable-next-line no-undef
return FRONTEND_VERSION;
},
/**
* Are both frontend and backend in the same version?
* @returns {boolean}
*/
isFrontendBackendVersionMatched() {
if (!this.info.version) {
return true;
}
return this.info.version === this.frontendVersion;
}
}, },
watch: { watch: {

View file

@ -75,6 +75,7 @@ export default {
}, },
methods: { methods: {
/** Update the theme color meta tag */
updateThemeColorMeta() { updateThemeColorMeta() {
if (this.theme === "dark") { if (this.theme === "dark") {
document.querySelector("#theme-color").setAttribute("content", "#161B22"); document.querySelector("#theme-color").setAttribute("content", "#161B22");

View file

@ -51,6 +51,7 @@ export default {
}; };
}, },
methods: { methods: {
/** Submit form data to add new status page */
async submit() { async submit() {
this.processing = true; this.processing = true;

View file

@ -289,39 +289,47 @@ export default {
}, },
methods: { methods: {
/** Request a test notification be sent for this monitor */
testNotification() { testNotification() {
this.$root.getSocket().emit("testNotification", this.monitor.id); this.$root.getSocket().emit("testNotification", this.monitor.id);
toast.success("Test notification is requested."); toast.success("Test notification is requested.");
}, },
/** Show dialog to confirm pause */
pauseDialog() { pauseDialog() {
this.$refs.confirmPause.show(); this.$refs.confirmPause.show();
}, },
/** Resume this monitor */
resumeMonitor() { resumeMonitor() {
this.$root.getSocket().emit("resumeMonitor", this.monitor.id, (res) => { this.$root.getSocket().emit("resumeMonitor", this.monitor.id, (res) => {
this.$root.toastRes(res); this.$root.toastRes(res);
}); });
}, },
/** Request that this monitor is paused */
pauseMonitor() { pauseMonitor() {
this.$root.getSocket().emit("pauseMonitor", this.monitor.id, (res) => { this.$root.getSocket().emit("pauseMonitor", this.monitor.id, (res) => {
this.$root.toastRes(res); this.$root.toastRes(res);
}); });
}, },
/** Show dialog to confirm deletion */
deleteDialog() { deleteDialog() {
this.$refs.confirmDelete.show(); this.$refs.confirmDelete.show();
}, },
/** Show dialog to confirm clearing events */
clearEventsDialog() { clearEventsDialog() {
this.$refs.confirmClearEvents.show(); this.$refs.confirmClearEvents.show();
}, },
/** Show dialog to confirm clearing heartbeats */
clearHeartbeatsDialog() { clearHeartbeatsDialog() {
this.$refs.confirmClearHeartbeats.show(); this.$refs.confirmClearHeartbeats.show();
}, },
/** Request that this monitor is deleted */
deleteMonitor() { deleteMonitor() {
this.$root.deleteMonitor(this.monitor.id, (res) => { this.$root.deleteMonitor(this.monitor.id, (res) => {
if (res.ok) { if (res.ok) {
@ -333,6 +341,7 @@ export default {
}); });
}, },
/** Request that this monitors events are cleared */
clearEvents() { clearEvents() {
this.$root.clearEvents(this.monitor.id, (res) => { this.$root.clearEvents(this.monitor.id, (res) => {
if (! res.ok) { if (! res.ok) {
@ -341,6 +350,7 @@ export default {
}); });
}, },
/** Request that this monitors heartbeats are cleared */
clearHeartbeats() { clearHeartbeats() {
this.$root.clearHeartbeats(this.monitor.id, (res) => { this.$root.clearHeartbeats(this.monitor.id, (res) => {
if (! res.ok) { if (! res.ok) {
@ -349,6 +359,11 @@ export default {
}); });
}, },
/**
* Return the correct title for the ping stat
* @param {boolean} [average=false] Is the statistic an average?
* @returns {string} Title formated dependant on monitor type
*/
pingTitle(average = false) { pingTitle(average = false) {
let translationPrefix = ""; let translationPrefix = "";
if (average) { if (average) {

View file

@ -48,6 +48,9 @@
<option value="sqlserver"> <option value="sqlserver">
SQL Server SQL Server
</option> </option>
<option value="postgres">
PostgreSQL
</option>
</optgroup> </optgroup>
</select> </select>
</div> </div>
@ -199,15 +202,21 @@
</div> </div>
</template> </template>
<!-- SQL Server --> <!-- SQL Server and PostgreSQL -->
<template v-if="monitor.type === 'sqlserver'"> <template v-if="monitor.type === 'sqlserver' || monitor.type === 'postgres'">
<div class="my-3"> <div class="my-3">
<label for="sqlserverConnectionString" class="form-label">SQL Server {{ $t("Connection String") }}</label> <label for="sqlConnectionString" class="form-label">{{ $t("Connection String") }}</label>
<input id="sqlserverConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control">
<template v-if="monitor.type === 'sqlserver'">
<input id="sqlConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control" placeholder="Server=<hostname>,<port>;Database=<your database>;User Id=<your user id>;Password=<your password>;Encrypt=<true/false>;TrustServerCertificate=<Yes/No>;Connection Timeout=<int>">
</template>
<template v-if="monitor.type === 'postgres'">
<input id="sqlConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control" placeholder="postgres://username:password@host:port/database">
</template>
</div> </div>
<div class="my-3"> <div class="my-3">
<label for="sqlserverQuery" class="form-label">SQL Server {{ $t("Query") }}</label> <label for="sqlQuery" class="form-label">{{ $t("Query") }}</label>
<textarea id="sqlserverQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea> <textarea id="sqlQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea>
</div> </div>
</template> </template>
@ -400,17 +409,17 @@
</div> </div>
<!-- HTTP Auth --> <!-- HTTP Auth -->
<h4 class="mt-5 mb-2">{{ $t("HTTP Authentication") }}</h4> <h4 class="mt-5 mb-2">{{ $t("Authentication") }}</h4>
<!-- Method --> <!-- Method -->
<div class="my-3"> <div class="my-3">
<label for="method" class="form-label">{{ $t("Method") }}</label> <label for="method" class="form-label">{{ $t("Method") }}</label>
<select id="method" v-model="monitor.authMethod" class="form-select"> <select id="method" v-model="monitor.authMethod" class="form-select">
<option :value="null"> <option :value="null">
None {{ $t("None") }}
</option> </option>
<option value="basic"> <option value="basic">
Basic {{ $t("HTTP Basic Auth") }}
</option> </option>
<option value="ntlm"> <option value="ntlm">
NTLM NTLM
@ -569,7 +578,7 @@ export default {
if (this.monitor.type === "dns") { if (this.monitor.type === "dns") {
this.monitor.port = "53"; this.monitor.port = "53";
} else { } else {
this.monitor.port = ""; this.monitor.port = undefined;
} }
} }
} }
@ -607,6 +616,7 @@ export default {
this.dnsresolvetypeOptions = dnsresolvetypeOptions; this.dnsresolvetypeOptions = dnsresolvetypeOptions;
}, },
methods: { methods: {
/** Initialize the edit monitor form */
init() { init() {
if (this.isAdd) { if (this.isAdd) {
@ -617,7 +627,6 @@ export default {
method: "GET", method: "GET",
interval: 60, interval: 60,
retryInterval: this.interval, retryInterval: this.interval,
databaseConnectionString: "Server=<hostname>,<port>;Database=<your database>;User Id=<your user id>;Password=<your password>;Encrypt=<true/false>;TrustServerCertificate=<Yes/No>;Connection Timeout=<int>",
maxretries: 0, maxretries: 0,
notificationIDList: {}, notificationIDList: {},
ignoreTls: false, ignoreTls: false,
@ -667,6 +676,10 @@ export default {
}, },
/**
* Validate form input
* @returns {boolean} Is the form input valid?
*/
isInputValid() { isInputValid() {
if (this.monitor.body) { if (this.monitor.body) {
try { try {
@ -687,6 +700,10 @@ export default {
return true; return true;
}, },
/**
* Submit the form data for processing
* @returns {void}
*/
async submit() { async submit() {
this.processing = true; this.processing = true;
@ -731,14 +748,20 @@ export default {
} }
}, },
// Added a Notification Event /**
// Enable it if the notification is added in EditMonitor.vue * Added a Notification Event
* Enable it if the notification is added in EditMonitor.vue
* @param {number} id ID of notification to add
*/
addedNotification(id) { addedNotification(id) {
this.monitor.notificationIDList[id] = true; this.monitor.notificationIDList[id] = true;
}, },
// Added a Proxy Event /**
// Enable it if the proxy is added in EditMonitor.vue * Added a Proxy Event
* Enable it if the proxy is added in EditMonitor.vue
* @param {number} id ID of proxy to add
*/
addedProxy(id) { addedProxy(id) {
this.monitor.proxyId = id; this.monitor.proxyId = id;
}, },

View file

@ -51,6 +51,11 @@ export default {
}, },
methods: { methods: {
/**
* Get the correct URL for the icon
* @param {string} icon Path for icon
* @returns {string} Correctly formatted path including port numbers
*/
icon(icon) { icon(icon) {
if (icon === "/icon.svg") { if (icon === "/icon.svg") {
return icon; return icon;
@ -86,6 +91,7 @@ export default {
.logo { .logo {
width: $logo-width; width: $logo-width;
height: $logo-width;
// Better when the image is loading // Better when the image is loading
min-height: 1px; min-height: 1px;

View file

@ -45,6 +45,7 @@ export default {
}, },
methods: { methods: {
/** Go back 1 in browser history */
goBack() { goBack() {
history.back(); history.back();
} }

View file

@ -121,13 +121,17 @@ export default {
methods: { methods: {
// For desktop only, mobile do nothing /**
* Load the general settings page
* For desktop only, on mobile do nothing
*/
loadGeneralPage() { loadGeneralPage() {
if (!this.currentPage && !this.$root.isMobile) { if (!this.currentPage && !this.$root.isMobile) {
this.$router.push("/settings/general"); this.$router.push("/settings/general");
} }
}, },
/** Load settings from server */
loadSettings() { loadSettings() {
this.$root.getSocket().emit("getSettings", (res) => { this.$root.getSocket().emit("getSettings", (res) => {
this.settings = res.data; this.settings = res.data;
@ -156,9 +160,16 @@ export default {
}); });
}, },
/**
* Callback for saving settings
* @callback saveSettingsCB
* @param {Object} res Result of operation
*/
/** /**
* Save Settings * Save Settings
* @param currentPassword (Optional) Only need for disableAuth to true * @param {saveSettingsCB} [callback]
* @param {string} [currentPassword] Only need for disableAuth to true
*/ */
saveSettings(callback, currentPassword) { saveSettings(callback, currentPassword) {
this.$root.getSocket().emit("setSettings", this.settings, currentPassword, (res) => { this.$root.getSocket().emit("setSettings", this.settings, currentPassword, (res) => {

View file

@ -71,6 +71,10 @@ export default {
}); });
}, },
methods: { methods: {
/**
* Submit form data for processing
* @returns {void}
*/
submit() { submit() {
this.processing = true; this.processing = true;

Some files were not shown because too many files have changed in this diff Show more