Merge branch 'master' into master

pull/1183/head
Moritz R 2 years ago committed by GitHub
commit 60e7824ff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

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

@ -151,9 +151,9 @@ You can discuss or ask for help in [issues](https://github.com/louislam/uptime-k
### 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.
https://www.reddit.com/r/UptimeKuma/
[r/Uptime kuma](https://www.reddit.com/r/UptimeKuma/)
## Contribute

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

@ -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;

@ -4,5 +4,5 @@ WORKDIR /app
# 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 && \
pip3 --no-cache-dir install apprise==0.9.8.3 && \
pip3 --no-cache-dir install apprise==0.9.9 && \
rm -rf /root/.cache

@ -11,7 +11,7 @@ WORKDIR /app
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 \
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/* && \
apt --yes autoremove

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

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

12226
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "uptime-kuma",
"version": "1.17.0-beta.0",
"version": "1.17.1",
"license": "MIT",
"repository": {
"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-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",
"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",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js",
@ -61,23 +61,15 @@
"build-dist-and-restart": "npm run build && npm run start-server-dev"
},
"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",
"@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0",
"axios": "~0.26.1",
"axios-cached-dns-resolve": "^3.0.6",
"axios-ntlm": "^1.3.0",
"badge-maker": "^3.3.1",
"bcryptjs": "~2.4.3",
"bootstrap": "5.1.3",
"bree": "~7.1.5",
"cacheable-lookup": "~6.0.4",
"chardet": "^1.3.0",
"chart.js": "~3.6.2",
"chartjs-adapter-dayjs": "~1.0.0",
"check-password-strength": "^2.0.5",
"cheerio": "^1.0.0-rc.10",
"chroma-js": "^2.1.2",
@ -85,11 +77,9 @@
"compare-versions": "~3.6.0",
"compression": "^1.7.4",
"dayjs": "^1.11.0",
"esm-wallaby": "^3.2.26",
"express": "~4.17.3",
"express-basic-auth": "~1.2.1",
"express-static-gzip": "^2.1.7",
"favico.js": "^0.3.10",
"form-data": "~4.0.0",
"http-graceful-shutdown": "~3.1.7",
"http-proxy-agent": "^5.0.0",
@ -104,64 +94,72 @@
"nodemailer": "~6.6.5",
"notp": "~2.0.3",
"password-hash": "~1.2.2",
"postcss-rtlcss": "~3.4.1",
"postcss-scss": "~4.0.3",
"prismjs": "^1.27.0",
"pg": "^8.7.3",
"pg-connection-string": "^2.5.0",
"prom-client": "~13.2.0",
"prometheus-api-metrics": "~3.2.1",
"qrcode": "~1.5.0",
"redbean-node": "0.1.4",
"socket.io": "~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",
"tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2",
"timezones-list": "~3.0.1",
"v-pagination-3": "~0.1.7",
"vue": "next",
"vue-chart-3": "3.0.9",
"vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4",
"vue-i18n": "~9.1.9",
"vue-image-crop-upload": "~3.0.3",
"vue-multiselect": "~3.0.0-alpha.2",
"vue-prism-editor": "^2.0.0-alpha.2",
"vue-qrcode": "~1.0.0",
"vue-router": "~4.0.14",
"vue-toastification": "~2.0.0-rc.5",
"vuedraggable": "~4.1.0"
"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",
"delay": "^5.0.0",
"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",
"lru-cache": "^7.7.1",
"npm-check-updates": "^12.5.9",
"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",
"typescript": "~4.4.4",
"v-pagination-3": "~0.1.7",
"vite": "~2.9.9",
"vite-plugin-compression": "^0.5.1",
"vue": "next",
"vue-chart-3": "3.0.9",
"vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4",
"vue-i18n": "~9.1.9",
"vue-image-crop-upload": "~3.0.3",
"vue-multiselect": "~3.0.0-alpha.2",
"vue-prism-editor": "^2.0.0-alpha.2",
"vue-qrcode": "~1.0.0",
"vue-router": "~4.0.14",
"vue-toastification": "~2.0.0-rc.5",
"vuedraggable": "~4.1.0",
"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

@ -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,
};

@ -22,7 +22,10 @@ async function sendNotificationList(socket) {
]);
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);

@ -59,6 +59,7 @@ class Database {
"patch-monitor-expiry-notification.sql": true,
"patch-status-page-footer-css.sql": true,
"patch-added-mqtt-monitor.sql": true,
"patch-add-clickable-status-page-link.sql": true,
"patch-add-sqlserver-monitor.sql": true,
"patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] },
};
@ -178,7 +179,13 @@ class Database {
} else {
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 {
@ -446,6 +453,23 @@ class Database {
this.backupWalPath = walPath + ".bak" + version;
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);
}
}
}
}

@ -31,7 +31,7 @@ class Group extends BeanModel {
*/
async getMonitorList() {
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
AND group_id = ?
ORDER BY monitor_group.weight

@ -7,7 +7,7 @@ dayjs.extend(timezone);
const axios = require("axios");
const { Prometheus } = require("../prometheus");
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 { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification");
@ -16,12 +16,7 @@ const { demoMode } = require("../config");
const version = require("../../package.json").version;
const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server");
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);
const { CacheableDnsHttpAgent } = require("../cacheable-dns-http-agent");
/**
* status:
@ -40,7 +35,13 @@ class Monitor extends BeanModel {
let obj = {
id: this.id,
name: this.name,
sendUrl: this.sendUrl,
};
if (this.sendUrl) {
obj.url = this.url;
}
if (showTags) {
obj.tags = await this.getTags();
}
@ -290,7 +291,7 @@ class Monitor extends BeanModel {
});
} else {
res = await axiosClient.request(options);
res = await axios.request(options);
}
bean.msg = `${res.status} - ${res.statusText}`;
@ -437,16 +438,19 @@ class Monitor extends BeanModel {
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,
headers: {
"Accept": "*/*",
"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)
rejectUnauthorized: !this.getIgnoreTls(),
}),
httpAgent: CacheableDnsHttpAgent.getHttpAgent({
maxCachedSessions: 0,
}),
maxRedirects: this.maxredirects,
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
@ -509,6 +513,14 @@ class Monitor extends BeanModel {
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.status = UP;
bean.ping = dayjs().valueOf() - startTime;

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

@ -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;

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

@ -1,40 +1,41 @@
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 Bark = require("./notification-providers/bark");
const ClickSendSMS = require("./notification-providers/clicksendsms");
const DingDing = require("./notification-providers/dingding");
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 Ntfy = require("./notification-providers/ntfy");
const Line = require("./notification-providers/line");
const LunaSea = require("./notification-providers/lunasea");
const Mattermost = require("./notification-providers/mattermost");
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 OneBot = require("./notification-providers/onebot");
const PagerDuty = require("./notification-providers/pagerduty");
const PromoSMS = require("./notification-providers/promosms");
const ClickSendSMS = require("./notification-providers/clicksendsms");
const Pushbullet = require("./notification-providers/pushbullet");
const PushDeer = require("./notification-providers/pushdeer");
const Pushover = require("./notification-providers/pushover");
const Pushy = require("./notification-providers/pushy");
const TechulusPush = require("./notification-providers/techulus-push");
const RocketChat = require("./notification-providers/rocket-chat");
const SerwerSMS = require("./notification-providers/serwersms");
const Signal = require("./notification-providers/signal");
const Slack = require("./notification-providers/slack");
const SMTP = require("./notification-providers/smtp");
const Stackfield = require("./notification-providers/stackfield");
const Teams = require("./notification-providers/teams");
const TechulusPush = require("./notification-providers/techulus-push");
const Telegram = require("./notification-providers/telegram");
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 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 {
@ -47,41 +48,42 @@ class Notification {
this.providerList = {};
const list = [
new Apprise(),
new Alerta(),
new AlertNow(),
new AliyunSms(),
new Apprise(),
new Bark(),
new ClickSendSMS(),
new DingDing(),
new Discord(),
new Teams(),
new Feishu(),
new GoogleChat(),
new Gorush(),
new Gotify(),
new Ntfy(),
new Line(),
new LunaSea(),
new Feishu(),
new Mattermost(),
new Matrix(),
new Mattermost(),
new Ntfy(),
new Octopush(),
new OneBot(),
new PagerDuty(),
new PromoSMS(),
new ClickSendSMS(),
new Pushbullet(),
new PushDeer(),
new Pushover(),
new Pushy(),
new TechulusPush(),
new RocketChat(),
new SerwerSMS(),
new Signal(),
new Slack(),
new SMTP(),
new Stackfield(),
new Teams(),
new TechulusPush(),
new Telegram(),
new Webhook(),
new Bark(),
new SerwerSMS(),
new Stackfield(),
new WeCom(),
new GoogleChat(),
new PagerDuty(),
new Gorush(),
new Alerta(),
new OneBot(),
new PushDeer(),
];
for (let item of list) {

@ -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;

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

@ -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,
};

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

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

@ -7,6 +7,7 @@ const { R } = require("redbean-node");
const { log } = require("../src/util");
const Database = require("./database");
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.
@ -71,6 +72,8 @@ class UptimeKumaServer {
}
}
CacheableDnsHttpAgent.registerGlobalAgent();
this.io = new Server(this.httpServer);
}

@ -11,7 +11,10 @@ const mqtt = require("mqtt");
const chroma = require("chroma-js");
const { badgeConstants } = require("./config");
const mssql = require("mssql");
const { Client } = require("pg");
const postgresConParse = require("pg-connection-string").parse;
const { NtlmClient } = require("axios-ntlm");
const { Settings } = require("./settings");
// From ping-lite
exports.WIN = /^win/.test(process.platform);
@ -237,10 +240,6 @@ exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype) {
*/
exports.mssqlQuery = function (connectionString, query) {
return new Promise((resolve, reject) => {
mssql.on("error", err => {
reject(err);
});
mssql.connect(connectionString).then(pool => {
return pool.request()
.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
* @param {string} key Key of setting to retrieve
* @returns {Promise<any>} Value
*/
exports.setting = async function (key) {
let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [
key,
]);
try {
const v = JSON.parse(value);
log.debug("util", `Get Setting: ${key}: ${v}`);
return v;
} catch (e) {
return value;
}
return await Settings.get(key);
};
/**
@ -281,70 +302,26 @@ exports.setting = async function (key) {
* @returns {Promise<void>}
*/
exports.setSetting = async function (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);
await Settings.set(key, value, type);
};
/**
* Get settings based on type
* @param {?string} type The type of setting
* @param {string} type The type of setting
* @returns {Promise<Bean>}
*/
exports.getSettings = async function (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;
return await Settings.getSettings(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
* @returns {Promise<void>}
*/
exports.setSettings = async function (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);
await Settings.setSettings(type, data);
};
// ssl-checker by @dyaa
@ -437,7 +414,7 @@ exports.checkCertificate = function (res) {
/**
* 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
* @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

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

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

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

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

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

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

@ -17,14 +17,17 @@
export default {
props: {
/** Size of the heartbeat bar */
size: {
type: String,
default: "big",
},
/** ID of the monitor */
monitorId: {
type: Number,
required: true,
},
/** Array of the monitors heartbeats */
heartbeatList: {
type: Array,
default: null,
@ -160,12 +163,19 @@ export default {
this.resize();
},
methods: {
/** Resize the heartbeat bar */
resize() {
if (this.$refs.wrap) {
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) {
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
},

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

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

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

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

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

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

@ -39,7 +39,27 @@
<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" />
{{ 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 v-if="showTags" class="tags">
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
@ -72,10 +92,12 @@ export default {
Tag,
},
props: {
/** Are we in edit mode? */
editMode: {
type: Boolean,
required: true,
},
/** Should tags be shown? */
showTags: {
type: Boolean,
}
@ -94,13 +116,50 @@ export default {
},
methods: {
/**
* Remove the specified group
* @param {number} index Index of group to remove
*/
removeGroup(index) {
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) {
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>
@ -119,6 +178,22 @@ export default {
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 {
transition: transform 0.5s;
}

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

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

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

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

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

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

@ -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>

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

@ -4,6 +4,11 @@
<object class="my-4" width="200" height="200" data="/icon.svg" />
<div class="fs-4 fw-bold">Uptime Kuma</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>
@ -46,6 +51,16 @@ export default {
}
.update-link {
font-size: 0.8em;
}
.frontend-version {
font-size: 0.9em;
color: #cccccc;
.dark & {
color: #333333;
}
}
</style>

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

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

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

@ -23,6 +23,7 @@
<div class="my-4 pt-4">
<h5 class="my-4 settings-subheading">{{ $t("settingsCertificateExpiry") }}</h5>
<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 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>

@ -68,7 +68,9 @@
<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.") }}
<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">
{{ $t("Current Password") }}
</label>
@ -108,7 +110,9 @@ export default {
return this.$root.cloudflared;
},
computed: {
settings() {
return this.$parent.$parent.$parent.settings;
},
},
watch: {
@ -120,14 +124,17 @@ export default {
this.$root.getSocket().emit(prefix + "leave");
},
methods: {
/** Start the Cloudflare tunnel */
start() {
this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken);
},
/** Stop the Cloudflare tunnel */
stop() {
this.$root.getSocket().emit(prefix + "stop", this.currentPassword, (res) => {
this.$root.toastRes(res);
});
},
/** Remove the token for the Cloudflare tunnel */
removeToken() {
this.$root.getSocket().emit(prefix + "removeToken");
this.cloudflareTunnelToken = "";

@ -90,162 +90,11 @@
<TwoFADialog ref="TwoFADialog" />
<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' ">
<p>Seguro que deseas <strong>deshabilitar la autenticación</strong>?</p>
<p>Es para <strong>quien implementa autenticación de terceros</strong> ante Uptime Kuma como por ejemplo Cloudflare Access.</p>
<p>Por favor usar con cuidado.</p>
</template>
<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>
<!-- eslint-disable-next-line vue/no-v-html -->
<p v-html="$t('disableauth.message1')"></p>
<!-- eslint-disable-next-line vue/no-v-html -->
<p v-html="$t('disableauth.message2')"></p>
<p>{{ $t("Please use this option carefully!") }}</p>
<div class="mb-3">
<label for="current-password2" class="form-label">
@ -303,6 +152,7 @@ export default {
},
methods: {
/** Check new passwords match before saving them */
savePassword() {
if (this.password.newPassword !== this.password.repeatNewPassword) {
this.invalidPassword = true;
@ -320,6 +170,7 @@ export default {
}
},
/** Disable authentication for web app access */
disableAuth() {
this.settings.disableAuth = true;
@ -332,6 +183,7 @@ export default {
}, this.password.currentPassword);
},
/** Enable authentication for web app access */
enableAuth() {
this.settings.disableAuth = false;
this.saveSettings();

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

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

@ -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
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.
5. Translate `src/components/settings/Security.vue` (search for a `Confirm` component with `rel="confirmDisableAuth"`).
6. Add it into `languageList` constant.
7. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done.
5. Add it into `languageList` constant.
6. 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. 😏

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

@ -100,6 +100,9 @@ export default {
"Update Password": "Aktualizovat heslo",
"Disable Auth": "Deaktivovat 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í",
Leave: "Odejít",
"I understand, please disable": "Rozumím, chci ji deaktivovat",

@ -77,6 +77,9 @@ export default {
"Update Password": "Passwort aktualisieren",
"Disable Auth": "Authentifizierung deaktivieren",
"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",
notificationDescription: "Benachrichtigungen müssen einem Monitor zugewiesen werden, damit diese funktionieren.",
Leave: "Verlassen",
@ -421,6 +424,7 @@ export default {
Next: "Weiter",
"The slug is already taken. Please choose another slug.": "Der Slug ist bereits in Verwendung. Bitte wähle einen anderen.",
"No Proxy": "Kein Proxy",
Authentication: "Authentifizierung",
"HTTP Basic Auth": "HTTP Basisauthentifizierung",
"New Status Page": "Neue Status-Seite",
"Page Not Found": "Seite nicht gefunden",

@ -101,6 +101,9 @@ export default {
"Update Password": "Update Password",
"Disable Auth": "Disable 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",
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: "Authentication",
"HTTP Basic Auth": "HTTP Basic Auth",
"New Status Page": "New Status Page",
"Page Not Found": "Page Not Found",
@ -538,4 +542,8 @@ export default {
"Container Name / ID": "Container Name / ID",
"Docker Host": "Docker Host",
"Docker Hosts": "Docker Hosts"
"ntfy Topic": "ntfy Topic",
"Domain": "Domain",
"Workstation": "Workstation",
disableCloudflaredNoAuthMsg: "You are in No Auth mode, password is not require.",
};

@ -84,6 +84,9 @@ export default {
"Update Password": "Actualizar contraseña",
"Disable Auth": "Deshabilitar 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",
Leave: "Salir",
"I understand, please disable": "Entiendo, por favor deshabilitar",

@ -87,6 +87,9 @@ export default {
"Update Password": "Uuenda salasõna",
"Disable Auth": "Lülita autentimine välja",
"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",
Leave: "Lahku",
"I understand, please disable": "Olen tutvunud riskidega, lülita välja",

@ -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.",
};

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

@ -100,6 +100,9 @@ export default {
"Update Password": "Spremi novu lozinku",
"Disable Auth": "Onemoguć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",
Leave: "Poništi",
"I understand, please disable": "Razumijem, svejedno onemogući",

@ -96,6 +96,9 @@ export default {
"Update Password": "Jelszó módosítása",
"Disable Auth": "Hitelesítés tiltása",
"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",
Leave: "Elhagy",
"I understand, please disable": "Megértettem, kérem tiltsa le",

@ -96,6 +96,9 @@ export default {
"Update Password": "Perbarui Kata Sandi",
"Disable Auth": "Nonaktifkan 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",
Leave: "Pergi",
"I understand, please disable": "Saya mengerti, silakan dinonaktifkan",

@ -100,6 +100,9 @@ export default {
"Update Password": "Modifica password",
"Disable Auth": "Disabilita 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",
Leave: "Annulla",
"I understand, please disable": "Lo capisco, disabilitare l'autenticazione.",

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

@ -96,6 +96,9 @@ export default {
"Update Password": "Oppdater passord",
"Disable Auth": "Deaktiver 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",
Leave: "Forlat",
"I understand, please disable": "Jeg forstår, vennligst deaktiver",

@ -90,8 +90,11 @@ export default {
"New Password": "Nieuw wachtwoord",
"Repeat New Password": "Herhaal nieuw wachtwoord",
"Update Password": "Vernieuw wachtwoord",
"Disable Auth": "Autorisatie uitschakelen",
"Enable Auth": "Autorisatie inschakelen",
"Disable Auth": "Authenticatie uitschakelen",
"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",
Leave: "Vertrekken",
"I understand, please disable": "Ik begrijp het, schakel a.u.b. uit",
@ -351,7 +354,7 @@ export default {
Discard: "Weggooien",
Cancel: "Annuleren",
"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",
serwersmsAPIUser: "API Gebruikersnaam (incl. webapi_ prefix)",
serwersmsAPIPassword: "API Wachtwoord",
@ -386,7 +389,7 @@ export default {
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.",
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",
Invalid: "Ongeldig",
AccessKeyId: "AccessKey ID",
@ -407,7 +410,7 @@ export default {
High: "Hoog",
Retry: "Opnieuw",
Topic: "Onderwerp",
"WeCom Bot Key": "WeCom Bot Sleutel",
"WeCom Bot Key": "WeCom Bot Key",
"Setup Proxy": "Proxy instellen",
"Proxy Protocol": "Proxy Protocol",
"Proxy Server": "Proxy Server",
@ -449,7 +452,6 @@ export default {
"Issuer:": "Uitgever:",
"Fingerprint:": "Vingerafruk:",
"No status pages": "Geen status pagina's",
"Domain Name Expiry Notification": "Domein Naam Verloop Notificatie",
Proxy: "Proxy",
"Date Created": "Datum Aangemaakt",
onebotHttpAddress: "OneBot HTTP Adres",
@ -460,6 +462,70 @@ export default {
onebotSafetyTips: "Voor de veiligheid moet een toegangssleutel worden ingesteld",
"PushDeer Key": "PushDeer Key",
"Footer Text": "Footer Tekst",
"Show Powered By": "Laat 'Mogeljik gemaakt door' zien",
"Show Powered By": "Laat \"Mogeljik gemaakt door\" zien",
"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.",
};

@ -96,6 +96,9 @@ export default {
"Update Password": "Zaktualizuj hasło",
"Disable Auth": "Wyłą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",
Leave: "Zostaw",
"I understand, please disable": "Rozumiem, proszę wyłączyć",
@ -428,6 +431,7 @@ export default {
Next: "Dalej",
"The slug is already taken. Please choose another slug.": "Ten symbol jest już zajęty. Proszę, wybierz inny.",
"No Proxy": "Bez proxy",
Authentication: "Uwierzytelnianie",
"HTTP Basic Auth": "Podstawowa autoryzacja HTTP",
"New Status Page": "Nowa strona statusu",
"Page Not Found": "Strona nie została znaleziona",

@ -96,6 +96,9 @@ export default {
"Update Password": "Atualizar Senha",
"Disable Auth": "Desativar 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",
Leave: "Sair",
"I understand, please disable": "Eu entendo, por favor desative.",

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

@ -100,6 +100,9 @@ export default {
"Update Password": "Posodobi geslo",
"Disable Auth": "Onemogoč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",
Leave: "Zapusti",
"I understand, please disable": "Razumem, prosim onemogočite",
@ -181,8 +184,8 @@ export default {
"Add a monitor": "Dodaj monitor",
"Edit Status Page": "Uredi statusno stran",
"Go to Dashboard": "Pojdi na nadzorno ploščo",
"Status Page": "Página de Status",
"Status Pages": "Página de Status",
"Status Page": "Statusna stran",
"Status Pages": "Statusne strani",
defaultNotificationName: "Moje {notification} Obvestilo ({number})",
here: "tukaj",
Required: "Obvezno",

@ -84,6 +84,9 @@ export default {
"Update Password": "Izmeni lozinku",
"Disable Auth": "Isključ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",
Leave: "Izađi",
"I understand, please disable": "Razumem, molim te isključi",

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

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

@ -101,6 +101,9 @@ export default {
"Update Password": "Şifreyi Değiştir",
"Disable Auth": "Şifreli girişi iptal 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",
Leave: "Ayrıl",
"I understand, please disable": "Evet farkındayım, iptal et",

@ -1,5 +1,5 @@
export default {
languageName: "Український",
languageName: "Українська",
checkEverySecond: "Перевірка кожні {0} секунд",
retriesDescription: "Максимальна кількість спроб перед позначенням сервісу як недоступного та надсиланням повідомлення",
ignoreTLSError: "Ігнорувати помилку TLS/SSL для сайтів HTTPS",
@ -7,11 +7,11 @@ export default {
maxRedirectDescription: "Максимальна кількість перенаправлень. Поставте 0, щоб вимкнути перенаправлення.",
acceptedStatusCodesDescription: "Виберіть коди статусів для визначення доступності сервісу.",
passwordNotMatchMsg: "Повторення паролю не збігається.",
notificationDescription: "Прив'яжіть повідомлення до моніторів.",
notificationDescription: "Прив'яжіть сповіщення до моніторів.",
keywordDescription: "Пошук слова в чистому HTML або JSON-відповіді (чутливо до регістру)",
pauseDashboardHome: "Пауза",
deleteMonitorMsg: "Ви дійсно хочете видалити цей монітор?",
deleteNotificationMsg: "Ви дійсно хочете видалити це повідомлення для всіх моніторів?",
deleteNotificationMsg: "Ви дійсно хочете видалити це сповіщення для всіх моніторів?",
resolverserverDescription: "Cloudflare є сервером за замовчуванням. Ви завжди можете змінити цей сервер.",
rrtypeDescription: "Виберіть тип ресурсного запису, який ви хочете відстежувати",
pauseMonitorMsg: "Ви дійсно хочете поставити на паузу?",
@ -54,7 +54,7 @@ export default {
Keyword: "Ключове слово",
"Friendly Name": "Ім'я",
URL: "URL",
Hostname: "Ім'я хоста",
Hostname: "Адреса хоста",
Port: "Порт",
"Heartbeat Interval": "Частота опитування",
Retries: "Спроб",
@ -63,7 +63,7 @@ export default {
"Max. Redirects": "Макс. кількість перенаправлень",
"Accepted Status Codes": "Припустимі коди статусу",
Save: "Зберегти",
Notifications: "Повідомлення",
Notifications: "Сповіщення",
"Not available, please setup.": "Доступних сповіщень немає, необхідно створити.",
"Setup Notification": "Створити сповіщення",
Light: "Світла",
@ -84,6 +84,9 @@ export default {
"Update Password": "Оновити пароль",
"Disable Auth": "Вимкнути авторизацію",
"Enable Auth": "Увімкнути авторизацію",
"disableauth.message1": "Ви впевнені, що бажаєте <strong>вимкнути авторизацію</strong>?",
"disableauth.message2": "Це підходить для <strong>тих, у кого встановлена інша авторизація</strong> пееред відкриттям Uptime Kuma, наприклад Cloudflare Access.",
"Please use this option carefully!": "Будь ласка, використовуйте з обережністю.",
Logout: "Вийти",
Leave: "Відміна",
"I understand, please disable": "Я розумію, все одно відключити",
@ -97,7 +100,7 @@ export default {
"No Monitors, please": "Моніторів немає, будь ласка",
"No Monitors": "Монітори відсутні",
"add one": "створіть новий",
"Notification Type": "Тип повідомлення",
"Notification Type": "Тип сповіщення",
Email: "Пошта",
Test: "Перевірка",
"Certificate Info": "Інформація про сертифікат",
@ -116,7 +119,7 @@ export default {
Events: "Події",
Heartbeats: "Опитування",
"Auto Get": "Авто-отримання",
enableDefaultNotificationDescription: "Для кожного нового монітора це повідомлення буде включено за замовчуванням. Ви все ще можете відключити повідомлення в кожному моніторі окремо.",
enableDefaultNotificationDescription: "Для кожного нового монітора це сповіщення буде включено за замовчуванням. Ви все ще можете відключити сповіщення в кожному моніторі окремо.",
"Default enabled": "Використовувати за промовчанням",
"Also apply to existing monitors": "Застосувати до існуючих моніторів",
Export: "Експорт",
@ -167,7 +170,7 @@ export default {
Purple: "Пурпурний",
Pink: "Рожевий",
"Search...": "Пошук...",
"Avg. Ping": "Середнє значення пінгу",
"Avg. Ping": "Середній пінг",
"Avg. Response": "Середній час відповіді",
"Entry Page": "Головна сторінка",
statusPageNothing: "Тут порожньо. Додайте групу або монітор.",
@ -207,7 +210,7 @@ export default {
"Push URL": "URL пуша",
needPushEvery: "До цієї URL необхідно звертатися кожні {0} секунд",
pushOptionalParams: "Опціональні параметри: {0}",
defaultNotificationName: "Моє повідомлення {notification} ({number})",
defaultNotificationName: "Моє сповіщення {notification} ({number})",
here: "тут",
Required: "Потрібно",
"Bot Token": "Токен бота",
@ -254,7 +257,7 @@ export default {
"User Key": "Ключ користувача",
Device: "Пристрій",
"Message Title": "Заголовок повідомлення",
"Notification Sound": "Звук повідомлення",
"Notification Sound": "Звук сповіщення",
"More info on:": "Більше інформації: {0}",
pushoverDesc1: "Екстренний пріоритет (2) має таймуут повтору за замовчуванням 30 секунд і закінчується через 1 годину.",
pushoverDesc2: "Якщо ви бажаєте надсилати повідомлення різним пристроям, необхідно заповнити поле Пристрій.",
@ -350,7 +353,8 @@ export default {
"Start or end with a-z 0-9 only": "Початок та закінчення імені лише на символи: a-z 0-9",
"No consecutive dashes --": "Заборонено використовувати тире --",
"HTTP Options": "HTTP Опції",
"Basic Auth": "HTTP Авторизація",
Authentication: "Аутентифікація",
"HTTP Basic Auth": "Базова HTTP",
PushByTechulus: "Push by Techulus",
clicksendsms: "ClickSend SMS",
GoogleChat: "Google Chat (тільки Google Workspace)",
@ -388,4 +392,139 @@ export default {
alertaAlertState: "Стан алерту",
alertaRecoverState: "Стан відновлення",
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: "Ви перебуваєте в режимі без авторизації, пароль не потрібен.",
};

@ -100,6 +100,9 @@ export default {
"Update Password": "Cập nhật mật khẩu",
"Disable Auth": "Tắ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",
Leave: "Rời",
"I understand, please disable": "Tôi hiểu, làm ơn hãy tắt!",

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

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

@ -13,6 +13,7 @@ export default {
pauseDashboardHome: "暫停",
deleteMonitorMsg: "您確定要刪除此監測器嗎?",
deleteNotificationMsg: "您確定要為所有監測器刪除此通知嗎?",
dnsPortDescription: "DNS 伺服器連接埠。預設為 53。您可以隨時變更連接埠。",
resolverserverDescription: "Cloudflare 為預設伺服器。您可以隨時更換解析伺服器。",
rrtypeDescription: "選擇您想要監測的資源記錄類型",
pauseMonitorMsg: "您確定要暫停嗎?",
@ -100,6 +101,9 @@ export default {
"Update Password": "更新密碼",
"Disable Auth": "停用驗證",
"Enable Auth": "啟用驗證",
"disableauth.message1": ">你是否要<strong>取消登入驗證</strong>",
"disableauth.message2": "此功能是設計給已有<strong>第三方認證</strong>的使用者,例如 Cloudflare Access。",
"Please use this option carefully!": "請謹慎使用。",
Logout: "登出",
Leave: "離開",
"I understand, please disable": "我了解了,請停用",
@ -329,6 +333,8 @@ export default {
info: "資訊",
warning: "警告",
danger: "危險",
error: "錯誤",
critical: "嚴重",
primary: "主要",
light: "淺色",
dark: "暗色",
@ -369,6 +375,13 @@ export default {
smtpDkimHashAlgo: "雜湊演算法 (選填)",
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
wayToGetPagerDutyKey: "您可以前往服務 -> 服務目錄 -> (選取服務) -> 整合 -> 新增整合以取得。您可以搜尋 \"Events API V2\"。詳細資訊 {0}",
"Integration Key": "整合金鑰",
"Integration URL": "整合網址",
"Auto resolve or acknowledged": "自動解決或認可",
"do nothing": "不進行任何操作",
"auto acknowledged": "自動認可",
"auto resolve": "自動解決",
gorush: "Gorush",
alerta: "Alerta",
alertaApiEndpoint: "API 端點",
@ -428,6 +441,7 @@ export default {
Next: "下一步",
"The slug is already taken. Please choose another slug.": "此 slug 已被使用。請選擇其他 slug。",
"No Proxy": "無 Proxy",
Authentication: "驗證",
"HTTP Basic Auth": "HTTP 基本驗證",
"New Status Page": "新狀態頁",
"Page Not Found": "找不到頁面",
@ -461,4 +475,65 @@ export default {
"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": "舊版 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: "您處於無驗證模式。無須輸入密碼。",
};

@ -18,14 +18,31 @@ export default {
},
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) {
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) {
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) {
let secondString;
if (second) {
@ -36,6 +53,12 @@ export default {
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) {
if (value !== undefined && value !== "") {
return dayjs.utc(value).tz(this.timezone).format(format);

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

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

@ -63,6 +63,12 @@ export default {
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) {
// No need to re-init
if (this.socket.initedSocketIO) {
@ -263,10 +269,18 @@ export default {
socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res);
},
/**
* The storage currently in use
* @returns {Storage}
*/
storage() {
return (this.remember) ? localStorage : sessionStorage;
},
/**
* Get payload of JWT cookie
* @returns {(Object|undefined)}
*/
getJWTPayload() {
const jwtToken = this.$root.storage().token;
@ -276,10 +290,18 @@ export default {
return undefined;
},
/**
* Get current socket
* @returns {Socket}
*/
getSocket() {
return socket;
},
/**
* Show success or error toast dependant on response status code
* @param {Object} res Response object
*/
toastRes(res) {
if (res.ok) {
toast.success(res.msg);
@ -288,14 +310,35 @@ export default {
}
},
/**
* Show a success toast
* @param {string} msg Message to show
*/
toastSuccess(msg) {
toast.success(msg);
},
/**
* Show an error toast
* @param {string} msg Message to show
*/
toastError(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) {
socket.emit("login", {
username,
@ -320,6 +363,10 @@ export default {
});
},
/**
* Log in using a token
* @param {string} token Token to log in with
*/
loginByToken(token) {
socket.emit("loginByToken", token, (res) => {
this.allowLoginDialog = true;
@ -333,6 +380,7 @@ export default {
});
},
/** Log out of the web application */
logout() {
socket.emit("logout", () => { });
this.storage().removeItem("token");
@ -342,26 +390,54 @@ export default {
this.clearData();
},
/**
* Callback for general socket requests
* @callback socketCB
* @param {Object} res Result of operation
*/
/** Prepare 2FA configuration */
prepare2FA(callback) {
socket.emit("prepare2FA", callback);
},
/**
* Save the current 2FA configuration
* @param {any} secret Unused
* @param {socketCB} callback
*/
save2FA(secret, callback) {
socket.emit("save2FA", callback);
},
/**
* Disable 2FA for this user
* @param {socketCB} callback
*/
disable2FA(callback) {
socket.emit("disable2FA", callback);
},
/**
* Verify the provided 2FA token
* @param {string} token Token to verify
* @param {socketCB} callback
*/
verifyToken(token, callback) {
socket.emit("verifyToken", token, callback);
},
/**
* Get current 2FA status
* @param {socketCB} callback
*/
twoFAStatus(callback) {
socket.emit("twoFAStatus", callback);
},
/**
* Get list of monitors
* @param {socketCB} callback
*/
getMonitorList(callback) {
if (! callback) {
callback = () => { };
@ -369,36 +445,74 @@ export default {
socket.emit("getMonitorList", callback);
},
/**
* Add a monitor
* @param {Object} monitor Object representing monitor to add
* @param {socketCB} callback
*/
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) {
socket.emit("deleteMonitor", monitorID, callback);
},
/** Clear the hearbeat list */
clearData() {
console.log("reset heartbeat list");
this.heartbeatList = {};
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) {
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) {
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) {
socket.emit("clearHeartbeats", monitorID, callback);
},
/**
* Clear all statistics
* @param {socketCB} callback
*/
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) {
socket.emit("getMonitorBeats", monitorID, period, callback);
}
@ -492,6 +606,28 @@ export default {
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: {

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

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

@ -289,39 +289,47 @@ export default {
},
methods: {
/** Request a test notification be sent for this monitor */
testNotification() {
this.$root.getSocket().emit("testNotification", this.monitor.id);
toast.success("Test notification is requested.");
},
/** Show dialog to confirm pause */
pauseDialog() {
this.$refs.confirmPause.show();
},
/** Resume this monitor */
resumeMonitor() {
this.$root.getSocket().emit("resumeMonitor", this.monitor.id, (res) => {
this.$root.toastRes(res);
});
},
/** Request that this monitor is paused */
pauseMonitor() {
this.$root.getSocket().emit("pauseMonitor", this.monitor.id, (res) => {
this.$root.toastRes(res);
});
},
/** Show dialog to confirm deletion */
deleteDialog() {
this.$refs.confirmDelete.show();
},
/** Show dialog to confirm clearing events */
clearEventsDialog() {
this.$refs.confirmClearEvents.show();
},
/** Show dialog to confirm clearing heartbeats */
clearHeartbeatsDialog() {
this.$refs.confirmClearHeartbeats.show();
},
/** Request that this monitor is deleted */
deleteMonitor() {
this.$root.deleteMonitor(this.monitor.id, (res) => {
if (res.ok) {
@ -333,6 +341,7 @@ export default {
});
},
/** Request that this monitors events are cleared */
clearEvents() {
this.$root.clearEvents(this.monitor.id, (res) => {
if (! res.ok) {
@ -341,6 +350,7 @@ export default {
});
},
/** Request that this monitors heartbeats are cleared */
clearHeartbeats() {
this.$root.clearHeartbeats(this.monitor.id, (res) => {
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) {
let translationPrefix = "";
if (average) {

@ -48,6 +48,9 @@
<option value="sqlserver">
SQL Server
</option>
<option value="postgres">
PostgreSQL
</option>
</optgroup>
</select>
</div>
@ -199,15 +202,21 @@
</div>
</template>
<!-- SQL Server -->
<template v-if="monitor.type === 'sqlserver'">
<!-- SQL Server and PostgreSQL -->
<template v-if="monitor.type === 'sqlserver' || monitor.type === 'postgres'">
<div class="my-3">
<label for="sqlserverConnectionString" class="form-label">SQL Server {{ $t("Connection String") }}</label>
<input id="sqlserverConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control">
<label for="sqlConnectionString" class="form-label">{{ $t("Connection String") }}</label>
<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 class="my-3">
<label for="sqlserverQuery" class="form-label">SQL Server {{ $t("Query") }}</label>
<textarea id="sqlserverQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea>
<label for="sqlQuery" class="form-label">{{ $t("Query") }}</label>
<textarea id="sqlQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea>
</div>
</template>
@ -400,17 +409,17 @@
</div>
<!-- HTTP Auth -->
<h4 class="mt-5 mb-2">{{ $t("HTTP Authentication") }}</h4>
<h4 class="mt-5 mb-2">{{ $t("Authentication") }}</h4>
<!-- Method -->
<div class="my-3">
<label for="method" class="form-label">{{ $t("Method") }}</label>
<select id="method" v-model="monitor.authMethod" class="form-select">
<option :value="null">
None
{{ $t("None") }}
</option>
<option value="basic">
Basic
{{ $t("HTTP Basic Auth") }}
</option>
<option value="ntlm">
NTLM
@ -569,7 +578,7 @@ export default {
if (this.monitor.type === "dns") {
this.monitor.port = "53";
} else {
this.monitor.port = "";
this.monitor.port = undefined;
}
}
}
@ -607,6 +616,7 @@ export default {
this.dnsresolvetypeOptions = dnsresolvetypeOptions;
},
methods: {
/** Initialize the edit monitor form */
init() {
if (this.isAdd) {
@ -617,7 +627,6 @@ export default {
method: "GET",
interval: 60,
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,
notificationIDList: {},
ignoreTls: false,
@ -667,6 +676,10 @@ export default {
},
/**
* Validate form input
* @returns {boolean} Is the form input valid?
*/
isInputValid() {
if (this.monitor.body) {
try {
@ -687,6 +700,10 @@ export default {
return true;
},
/**
* Submit the form data for processing
* @returns {void}
*/
async submit() {
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) {
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) {
this.monitor.proxyId = id;
},

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

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

@ -121,13 +121,17 @@ export default {
methods: {
// For desktop only, mobile do nothing
/**
* Load the general settings page
* For desktop only, on mobile do nothing
*/
loadGeneralPage() {
if (!this.currentPage && !this.$root.isMobile) {
this.$router.push("/settings/general");
}
},
/** Load settings from server */
loadSettings() {
this.$root.getSocket().emit("getSettings", (res) => {
this.settings = res.data;
@ -156,9 +160,16 @@ export default {
});
},
/**
* Callback for saving settings
* @callback saveSettingsCB
* @param {Object} res Result of operation
*/
/**
* 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) {
this.$root.getSocket().emit("setSettings", this.settings, currentPassword, (res) => {

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

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

Loading…
Cancel
Save