# Conflicts: # package-lock.json # package.json # src/languages/en.jspull/719/head
commit
cb43ecb46e
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
"rootDir": "..",
|
||||||
|
"testRegex": "./test/backend.spec.js",
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
"rootDir": ".",
|
"rootDir": "..",
|
||||||
"testRegex": "./test/frontend.spec.js",
|
"testRegex": "./test/frontend.spec.js",
|
||||||
};
|
};
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD method TEXT default 'GET' not null;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD body TEXT default null;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD headers TEXT default null;
|
||||||
|
|
||||||
|
COMMIT;
|
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
apps: [{
|
||||||
|
name: "uptime-kuma",
|
||||||
|
script: "./server/server.js",
|
||||||
|
}]
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
console.log("Downloading dist");
|
||||||
|
const https = require("https");
|
||||||
|
const tar = require("tar");
|
||||||
|
|
||||||
|
const packageJSON = require("../package.json");
|
||||||
|
const fs = require("fs");
|
||||||
|
const version = packageJSON.version;
|
||||||
|
|
||||||
|
const filename = "dist.tar.gz";
|
||||||
|
|
||||||
|
const url = `https://github.com/louislam/uptime-kuma/releases/download/${version}/${filename}`;
|
||||||
|
download(url);
|
||||||
|
|
||||||
|
function download(url) {
|
||||||
|
console.log(url);
|
||||||
|
|
||||||
|
https.get(url, (response) => {
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
console.log("Extracting dist...");
|
||||||
|
|
||||||
|
if (fs.existsSync("./dist")) {
|
||||||
|
|
||||||
|
if (fs.existsSync("./dist-backup")) {
|
||||||
|
fs.rmdirSync("./dist-backup", {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.renameSync("./dist", "./dist-backup");
|
||||||
|
}
|
||||||
|
|
||||||
|
const tarStream = tar.x({
|
||||||
|
cwd: "./",
|
||||||
|
});
|
||||||
|
|
||||||
|
tarStream.on("close", () => {
|
||||||
|
fs.rmdirSync("./dist-backup", {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
console.log("Done");
|
||||||
|
});
|
||||||
|
|
||||||
|
tarStream.on("error", () => {
|
||||||
|
if (fs.existsSync("./dist-backup")) {
|
||||||
|
fs.renameSync("./dist-backup", "./dist");
|
||||||
|
}
|
||||||
|
console.log("Done");
|
||||||
|
});
|
||||||
|
|
||||||
|
response.pipe(tarStream);
|
||||||
|
} else if (response.statusCode === 302) {
|
||||||
|
download(response.headers.location);
|
||||||
|
} else {
|
||||||
|
console.log("dist not found");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
const args = require("args-parser")(process.argv);
|
||||||
|
const demoMode = args["demo"] || false;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
args,
|
||||||
|
demoMode
|
||||||
|
};
|
@ -0,0 +1,108 @@
|
|||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const { DOWN, UP } = require("../../src/util");
|
||||||
|
const { default: axios } = require("axios");
|
||||||
|
const Crypto = require("crypto");
|
||||||
|
const qs = require("qs");
|
||||||
|
|
||||||
|
class AliyunSMS extends NotificationProvider {
|
||||||
|
name = "AliyunSMS";
|
||||||
|
|
||||||
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
let okMsg = "Sent Successfully.";
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (heartbeatJSON != null) {
|
||||||
|
let msgBody = JSON.stringify({
|
||||||
|
name: monitorJSON["name"],
|
||||||
|
time: heartbeatJSON["time"],
|
||||||
|
status: this.statusToString(heartbeatJSON["status"]),
|
||||||
|
msg: heartbeatJSON["msg"],
|
||||||
|
});
|
||||||
|
if (this.sendSms(notification, msgBody)) {
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let msgBody = JSON.stringify({
|
||||||
|
name: "",
|
||||||
|
time: "",
|
||||||
|
status: "",
|
||||||
|
msg: msg,
|
||||||
|
});
|
||||||
|
if (this.sendSms(notification, msgBody)) {
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.throwGeneralAxiosError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendSms(notification, msgbody) {
|
||||||
|
let params = {
|
||||||
|
PhoneNumbers: notification.phonenumber,
|
||||||
|
TemplateCode: notification.templateCode,
|
||||||
|
SignName: notification.signName,
|
||||||
|
TemplateParam: msgbody,
|
||||||
|
AccessKeyId: notification.accessKeyId,
|
||||||
|
Format: "JSON",
|
||||||
|
SignatureMethod: "HMAC-SHA1",
|
||||||
|
SignatureVersion: "1.0",
|
||||||
|
SignatureNonce: Math.random().toString(),
|
||||||
|
Timestamp: new Date().toISOString(),
|
||||||
|
Action: "SendSms",
|
||||||
|
Version: "2017-05-25",
|
||||||
|
};
|
||||||
|
|
||||||
|
params.Signature = this.sign(params, notification.secretAccessKey);
|
||||||
|
let config = {
|
||||||
|
method: "POST",
|
||||||
|
url: "http://dysmsapi.aliyuncs.com/",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
data: qs.stringify(params),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await axios(config);
|
||||||
|
if (result.data.Message == "OK") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Aliyun request sign */
|
||||||
|
sign(param, AccessKeySecret) {
|
||||||
|
let param2 = {};
|
||||||
|
let data = [];
|
||||||
|
|
||||||
|
let oa = Object.keys(param).sort();
|
||||||
|
|
||||||
|
for (let i = 0; i < oa.length; i++) {
|
||||||
|
let key = oa[i];
|
||||||
|
param2[key] = param[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key in param2) {
|
||||||
|
data.push(`${encodeURIComponent(key)}=${encodeURIComponent(param2[key])}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let StringToSign = `POST&${encodeURIComponent("/")}&${encodeURIComponent(data.join("&"))}`;
|
||||||
|
return Crypto
|
||||||
|
.createHmac("sha1", `${AccessKeySecret}&`)
|
||||||
|
.update(Buffer.from(StringToSign))
|
||||||
|
.digest("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
statusToString(status) {
|
||||||
|
switch (status) {
|
||||||
|
case DOWN:
|
||||||
|
return "DOWN";
|
||||||
|
case UP:
|
||||||
|
return "UP";
|
||||||
|
default:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AliyunSMS;
|
@ -0,0 +1,79 @@
|
|||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const { DOWN, UP } = require("../../src/util");
|
||||||
|
const { default: axios } = require("axios");
|
||||||
|
const Crypto = require("crypto");
|
||||||
|
|
||||||
|
class DingDing extends NotificationProvider {
|
||||||
|
name = "DingDing";
|
||||||
|
|
||||||
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
let okMsg = "Sent Successfully.";
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (heartbeatJSON != null) {
|
||||||
|
let params = {
|
||||||
|
msgtype: "markdown",
|
||||||
|
markdown: {
|
||||||
|
title: monitorJSON["name"],
|
||||||
|
text: `## [${this.statusToString(heartbeatJSON["status"])}] \n > ${heartbeatJSON["msg"]} \n > Time(UTC):${heartbeatJSON["time"]}`,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (this.sendToDingDing(notification, params)) {
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let params = {
|
||||||
|
msgtype: "text",
|
||||||
|
text: {
|
||||||
|
content: msg
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (this.sendToDingDing(notification, params)) {
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.throwGeneralAxiosError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendToDingDing(notification, params) {
|
||||||
|
let timestamp = Date.now();
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
url: `${notification.webHookUrl}×tamp=${timestamp}&sign=${encodeURIComponent(this.sign(timestamp, notification.secretKey))}`,
|
||||||
|
data: JSON.stringify(params),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await axios(config);
|
||||||
|
if (result.data.errmsg == "ok") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** DingDing sign */
|
||||||
|
sign(timestamp, secretKey) {
|
||||||
|
return Crypto
|
||||||
|
.createHmac("sha256", Buffer.from(secretKey, "utf8"))
|
||||||
|
.update(Buffer.from(`${timestamp}\n${secretKey}`, "utf8"))
|
||||||
|
.digest("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
statusToString(status) {
|
||||||
|
switch (status) {
|
||||||
|
case DOWN:
|
||||||
|
return "DOWN";
|
||||||
|
case UP:
|
||||||
|
return "UP";
|
||||||
|
default:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DingDing;
|
@ -0,0 +1,83 @@
|
|||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const axios = require("axios");
|
||||||
|
const { DOWN, UP } = require("../../src/util");
|
||||||
|
|
||||||
|
class Feishu extends NotificationProvider {
|
||||||
|
name = "Feishu";
|
||||||
|
|
||||||
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
let okMsg = "Sent Successfully.";
|
||||||
|
let feishuWebHookUrl = notification.feishuWebHookUrl;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (heartbeatJSON == null) {
|
||||||
|
let testdata = {
|
||||||
|
msg_type: "text",
|
||||||
|
content: {
|
||||||
|
text: msg,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await axios.post(feishuWebHookUrl, testdata);
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heartbeatJSON["status"] == DOWN) {
|
||||||
|
let downdata = {
|
||||||
|
msg_type: "post",
|
||||||
|
content: {
|
||||||
|
post: {
|
||||||
|
zh_cn: {
|
||||||
|
title: "UptimeKuma Alert: " + monitorJSON["name"],
|
||||||
|
content: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
tag: "text",
|
||||||
|
text:
|
||||||
|
"[Down] " +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
"\nTime (UTC): " +
|
||||||
|
heartbeatJSON["time"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await axios.post(feishuWebHookUrl, downdata);
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heartbeatJSON["status"] == UP) {
|
||||||
|
let updata = {
|
||||||
|
msg_type: "post",
|
||||||
|
content: {
|
||||||
|
post: {
|
||||||
|
zh_cn: {
|
||||||
|
title: "UptimeKuma Alert: " + monitorJSON["name"],
|
||||||
|
content: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
tag: "text",
|
||||||
|
text:
|
||||||
|
"[Up] " +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
"\nTime (UTC): " +
|
||||||
|
heartbeatJSON["time"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await axios.post(feishuWebHookUrl, updata);
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.throwGeneralAxiosError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Feishu;
|
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="accessKeyId" class="form-label">{{ $t("AccessKeyId") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="accessKeyId" v-model="$parent.notification.accessKeyId" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<label for="secretAccessKey" class="form-label">{{ $t("SecretAccessKey") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="secretAccessKey" v-model="$parent.notification.secretAccessKey" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<label for="phonenumber" class="form-label">{{ $t("Phonenumber") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="phonenumber" v-model="$parent.notification.phonenumber" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<label for="templateCode" class="form-label">{{ $t("TemplateCode") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="templateCode" v-model="$parent.notification.templateCode" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<label for="signName" class="form-label">{{ $t("SignName") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="signName" v-model="$parent.notification.signName" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
<p>Sms template must contain parameters: <br> <code>${name} ${time} ${status} ${msg}</code></p>
|
||||||
|
<i18n-t tag="p" keypath="Read more:">
|
||||||
|
<a href="https://help.aliyun.com/document_detail/101414.html" target="_blank">https://help.aliyun.com/document_detail/101414.html</a>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="WebHookUrl" class="form-label">{{ $t("WebHookUrl") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="WebHookUrl" v-model="$parent.notification.webHookUrl" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<label for="secretKey" class="form-label">{{ $t("SecretKey") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="secretKey" v-model="$parent.notification.secretKey" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
<p>For safety, must use secret key</p>
|
||||||
|
<i18n-t tag="p" keypath="Read more:">
|
||||||
|
<a href="https://developers.dingtalk.com/document/robots/custom-robot-access" target="_blank">https://developers.dingtalk.com/document/robots/custom-robot-access</a>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="Feishu-WebHookUrl" class="form-label">{{ $t("Feishu WebHookUrl") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="Feishu-WebHookUrl" v-model="$parent.notification.feishuWebHookUrl" type="text" class="form-control" required>
|
||||||
|
<div class="form-text">
|
||||||
|
<p><span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}</p>
|
||||||
|
</div>
|
||||||
|
<i18n-t tag="div" keypath="wayToGetTeamsURL" class="form-text">
|
||||||
|
<a
|
||||||
|
href="https://www.feishu.cn/hc/zh-CN/articles/360024984973"
|
||||||
|
target="_blank"
|
||||||
|
>{{ $t("here") }}</a>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -0,0 +1,285 @@
|
|||||||
|
export default {
|
||||||
|
languageName: "Bahasa Indonesia (Indonesian)",
|
||||||
|
checkEverySecond: "Cek Setiap {0} detik.",
|
||||||
|
retryCheckEverySecond: "Coba lagi setiap {0} detik.",
|
||||||
|
retriesDescription: "Percobaan ulang maksimum sebelum layanan dinyatakan tidak aktif dan notifikasi dikirim",
|
||||||
|
ignoreTLSError: "Abaikan kesalahan TLS/SSL untuk situs web HTTPS",
|
||||||
|
upsideDownModeDescription: "Balikkan statusnya. Jika layanan dapat dijangkau, TIDAK AKTIF.",
|
||||||
|
maxRedirectDescription: "Jumlah maksimum pengalihan untuk diikuti. Setel ke 0 untuk menonaktifkan pengalihan.",
|
||||||
|
acceptedStatusCodesDescription: "Pilih kode status yang dianggap sebagai tanggapan yang berhasil.",
|
||||||
|
passwordNotMatchMsg: "Sandi kedua tidak cocok.",
|
||||||
|
notificationDescription: "Harap atur notifikasi ke monitor agar berfungsi.",
|
||||||
|
keywordDescription: "Cari kata kunci dalam code html atau JSON huruf besar-kecil berpengaruh",
|
||||||
|
pauseDashboardHome: "Jeda",
|
||||||
|
deleteMonitorMsg: "Apakah Anda mau menghapus monitor ini?",
|
||||||
|
deleteNotificationMsg: "Apakah Anda mau menghapus notifikasi ini untuk semua monitor?",
|
||||||
|
resoverserverDescription: "Cloudflare adalah server bawaan, Anda dapat mengubah server resolver kapan saja.",
|
||||||
|
rrtypeDescription: "Pilih RR-Type yang mau Anda monitor",
|
||||||
|
pauseMonitorMsg: "Apakah Anda yakin mau menjeda?",
|
||||||
|
enableDefaultNotificationDescription: "Untuk setiap monitor baru, notifikasi ini akan diaktifkan secara bawaan. Anda masih dapat menonaktifkan notifikasi secara terpisah untuk setiap monitor.",
|
||||||
|
clearEventsMsg: "Apakah Anda yakin mau menghapus semua event di monitor ini?",
|
||||||
|
clearHeartbeatsMsg: "Apakah Anda yakin mau menghapus semua heartbeats di monitor ini?",
|
||||||
|
confirmClearStatisticsMsg: "Apakah Anda yakin mau menghapus semua statistik?",
|
||||||
|
importHandleDescription: "Pilih 'Lewati yang ada' jika Anda ingin melewati setiap monitor atau notifikasi dengan nama yang sama. 'Timpa' akan menghapus setiap monitor dan notifikasi yang ada.",
|
||||||
|
confirmImportMsg: "Apakah Anda yakin untuk mengimpor cadangan? Pastikan Anda telah memilih opsi impor yang tepat.",
|
||||||
|
twoFAVerifyLabel: "Silakan ketik token Anda untuk memverifikasi bahwa 2FA berfungsi",
|
||||||
|
tokenValidSettingsMsg: "Tokennya benar! Anda sekarang dapat menyimpan pengaturan 2FA.",
|
||||||
|
confirmEnableTwoFAMsg: "Apakah Anda yakin ingin mengaktifkan 2FA?",
|
||||||
|
confirmDisableTwoFAMsg: "Apakah Anda yakin ingin menonaktifkan 2FA?",
|
||||||
|
Settings: "Pengaturan",
|
||||||
|
Dashboard: "Dasbor",
|
||||||
|
"New Update": "Pembaruan Baru",
|
||||||
|
Language: "Bahasa",
|
||||||
|
Appearance: "Tampilan",
|
||||||
|
Theme: "Tema",
|
||||||
|
General: "Umum",
|
||||||
|
Version: "Versi",
|
||||||
|
"Check Update On GitHub": "Cek Pembaruan di GitHub",
|
||||||
|
List: "Daftar",
|
||||||
|
Add: "Tambah",
|
||||||
|
"Add New Monitor": "Tambah Monitor Baru",
|
||||||
|
"Quick Stats": "Statistik",
|
||||||
|
Up: "Aktif",
|
||||||
|
Down: "Tidak Aktif",
|
||||||
|
Pending: "Tertunda",
|
||||||
|
Unknown: "Tidak diketahui",
|
||||||
|
Pause: "Jeda",
|
||||||
|
Name: "Nama",
|
||||||
|
Status: "Status",
|
||||||
|
DateTime: "Tanggal Waktu",
|
||||||
|
Message: "Pesan",
|
||||||
|
"No important events": "Tidak ada peristiwa penting",
|
||||||
|
Resume: "Lanjut",
|
||||||
|
Edit: "Ubah",
|
||||||
|
Delete: "Hapus",
|
||||||
|
Current: "Saat ini",
|
||||||
|
Uptime: "Waktu aktif",
|
||||||
|
"Cert Exp.": "Cert Exp.",
|
||||||
|
days: "hari-hari",
|
||||||
|
day: "hari",
|
||||||
|
"-day": "-hari",
|
||||||
|
hour: "Jam",
|
||||||
|
"-hour": "-Jam",
|
||||||
|
Response: "Tanggapan",
|
||||||
|
Ping: "Ping",
|
||||||
|
"Monitor Type": "Tipe Monitor",
|
||||||
|
Keyword: "Keyword",
|
||||||
|
"Friendly Name": "Nama yang Ramah",
|
||||||
|
URL: "URL",
|
||||||
|
Hostname: "Hostname",
|
||||||
|
Port: "Port",
|
||||||
|
"Heartbeat Interval": "Jarak Waktu Heartbeat ",
|
||||||
|
Retries: "Coba lagi",
|
||||||
|
"Heartbeat Retry Interval": "Jarak Waktu Heartbeat Mencoba kembali ",
|
||||||
|
Advanced: "Tingkat Lanjut",
|
||||||
|
"Upside Down Mode": "Mode Terbalik",
|
||||||
|
"Max. Redirects": "Maksimal Pengalihan",
|
||||||
|
"Accepted Status Codes": "Kode Status yang Diterima",
|
||||||
|
Save: "Simpan",
|
||||||
|
Notifications: "Notifikasi",
|
||||||
|
"Not available, please setup.": "Tidak tersedia, silakan atur.",
|
||||||
|
"Setup Notification": "Setel Notifikasi",
|
||||||
|
Light: "Terang",
|
||||||
|
Dark: "Gelap",
|
||||||
|
Auto: "Otomatis",
|
||||||
|
"Theme - Heartbeat Bar": "Tema - Heartbeat Bar",
|
||||||
|
Normal: "Normal",
|
||||||
|
Bottom: "Bawah",
|
||||||
|
None: "Tidak ada",
|
||||||
|
Timezone: "Zona Waktu",
|
||||||
|
"Search Engine Visibility": "Visibilitas Mesin Pencari",
|
||||||
|
"Allow indexing": "Mengizinkan untuk diindex",
|
||||||
|
"Discourage search engines from indexing site": "Mencegah mesin pencari untuk mengindex situs",
|
||||||
|
"Change Password": "Ganti Sandi",
|
||||||
|
"Current Password": "Sandi Lama",
|
||||||
|
"New Password": "Sandi Baru",
|
||||||
|
"Repeat New Password": "Ulangi Sandi Baru",
|
||||||
|
"Update Password": "Perbarui Kata Sandi",
|
||||||
|
"Disable Auth": "Nonaktifkan Autentikasi",
|
||||||
|
"Enable Auth": "Aktifkan Autentikasi",
|
||||||
|
Logout: "Keluar",
|
||||||
|
Leave: "Pergi",
|
||||||
|
"I understand, please disable": "Saya mengerti, silakan dinonaktifkan",
|
||||||
|
Confirm: "Konfirmasi",
|
||||||
|
Yes: "Ya",
|
||||||
|
No: "Tidak",
|
||||||
|
Username: "Nama Pengguna",
|
||||||
|
Password: "Sandi",
|
||||||
|
"Remember me": "Ingat saya",
|
||||||
|
Login: "Masuk",
|
||||||
|
"No Monitors, please": "Tidak ada monitor, silakan",
|
||||||
|
"add one": "tambahkan satu",
|
||||||
|
"Notification Type": "Tipe Notifikasi",
|
||||||
|
Email: "Surel",
|
||||||
|
Test: "Tes",
|
||||||
|
"Certificate Info": "Info Sertifikasi",
|
||||||
|
"Resolver Server": "Resolver Server",
|
||||||
|
"Resource Record Type": "Resource Record Type",
|
||||||
|
"Last Result": "Hasil Terakhir",
|
||||||
|
"Create your admin account": "Buat admin akun Anda",
|
||||||
|
"Repeat Password": "Ulangi Sandi",
|
||||||
|
"Import Backup": "Impor Cadangan",
|
||||||
|
"Export Backup": "Expor Cadangan",
|
||||||
|
Export: "Expor",
|
||||||
|
Import: "Impor",
|
||||||
|
respTime: "Tanggapan. Waktu (milidetik)",
|
||||||
|
notAvailableShort: "N/A",
|
||||||
|
"Default enabled": "Bawaan diaktifkan",
|
||||||
|
"Apply on all existing monitors": "Terapkan pada semua monitor yang ada",
|
||||||
|
Create: "Buat",
|
||||||
|
"Clear Data": "Bersihkan Data",
|
||||||
|
Events: "Peristiwa",
|
||||||
|
Heartbeats: "Heartbeats",
|
||||||
|
"Auto Get": "Ambil Otomatis",
|
||||||
|
backupDescription: "Anda dapat mencadangkan semua monitor dan semua notifikasi ke dalam berkas JSON.",
|
||||||
|
backupDescription2: "Catatan: Data sejarah dan peristiwa tidak disertakan.",
|
||||||
|
backupDescription3: "Data sensitif seperti notifikasi token disertakan dalam berkas ekspor, harap simpan dengan hati-hati.",
|
||||||
|
alertNoFile: "Silakan pilih berkas untuk diimpor.",
|
||||||
|
alertWrongFileType: "Silakan pilih berkas JSON.",
|
||||||
|
"Clear all statistics": "Hapus semua statistik",
|
||||||
|
"Skip existing": "Lewati yang ada",
|
||||||
|
Overwrite: "Timpa",
|
||||||
|
Options: "Opsi",
|
||||||
|
"Keep both": "Simpan keduanya",
|
||||||
|
"Verify Token": "Verifikasi Token",
|
||||||
|
"Setup 2FA": "Pengaturan 2FA",
|
||||||
|
"Enable 2FA": "Aktifkan 2FA",
|
||||||
|
"Disable 2FA": "Nonaktifkan 2FA",
|
||||||
|
"2FA Settings": "Pengaturan 2FA",
|
||||||
|
"Two Factor Authentication": "Autentikasi Dua Faktor",
|
||||||
|
Active: "Aktif",
|
||||||
|
Inactive: "Tidak Aktif",
|
||||||
|
Token: "Token",
|
||||||
|
"Show URI": "Lihat URI",
|
||||||
|
Tags: "Tanda",
|
||||||
|
"Add New below or Select...": "Tambahkan Baru di bawah atau Pilih...",
|
||||||
|
"Tag with this name already exist.": "Tanda dengan nama ini sudah ada.",
|
||||||
|
"Tag with this value already exist.": "Tanda dengan nilai ini sudah ada.",
|
||||||
|
color: "warna",
|
||||||
|
"value (optional)": "nilai (harus diisi)",
|
||||||
|
Gray: "Abu-abu",
|
||||||
|
Red: "Merah",
|
||||||
|
Orange: "Jingga",
|
||||||
|
Green: "Hijau",
|
||||||
|
Blue: "Biru",
|
||||||
|
Indigo: "Biru Tua",
|
||||||
|
Purple: "Ungu",
|
||||||
|
Pink: "Merah Muda",
|
||||||
|
"Search...": "Cari...",
|
||||||
|
"Avg. Ping": "Rata-rata Ping",
|
||||||
|
"Avg. Response": "Rata-rata Tanggapan",
|
||||||
|
"Entry Page": "Halaman Masuk",
|
||||||
|
statusPageNothing: "Tidak ada di sini, silakan tambahkan grup atau monitor.",
|
||||||
|
"No Services": "Tidak ada Layanan",
|
||||||
|
"All Systems Operational": "Semua Sistem Berfungsi",
|
||||||
|
"Partially Degraded Service": "Layanan Terdegradasi Sebagian",
|
||||||
|
"Degraded Service": "Layanan Terdegradasi",
|
||||||
|
"Add Group": "Tambah Grup",
|
||||||
|
"Add a monitor": "Tambah monitor",
|
||||||
|
"Edit Status Page": "Edit Halaman Status",
|
||||||
|
"Go to Dashboard": "Pergi ke Dasbor",
|
||||||
|
"Status Page": "Halaman Status",
|
||||||
|
// Start notification form
|
||||||
|
defaultNotificationName: "{notification} saya Peringatan ({number})",
|
||||||
|
here: "di sini",
|
||||||
|
"Required": "Dibutuhkan",
|
||||||
|
"telegram": "Telegram",
|
||||||
|
"Bot Token": "Bot Token",
|
||||||
|
"You can get a token from": "Anda bisa mendapatkan token dari",
|
||||||
|
"Chat ID": "Chat ID",
|
||||||
|
supportTelegramChatID: "Mendukung Obrolan Langsung / Grup / Channel Chat ID",
|
||||||
|
wayToGetTelegramChatID: "Anda bisa mendapatkan chat id Anda dengan mengirim pesan ke bot dan pergi ke url ini untuk melihat chat_id:",
|
||||||
|
"YOUR BOT TOKEN HERE": "BOT TOKEN ANDA DI SINI",
|
||||||
|
chatIDNotFound: "Chat ID tidak ditemukan, tolong kirim pesan ke bot ini dulu",
|
||||||
|
"webhook": "Webhook",
|
||||||
|
"Post URL": "Post URL",
|
||||||
|
"Content Type": "Tipe konten",
|
||||||
|
webhookJsonDesc: "{0} bagus untuk peladen http modern seperti express.js",
|
||||||
|
webhookFormDataDesc: "{multipart} bagus untuk PHP, Anda hanya perlu mengurai json dengan {decodeFunction}",
|
||||||
|
"smtp": "Surel (SMTP)",
|
||||||
|
secureOptionNone: "None / STARTTLS (25, 587)",
|
||||||
|
secureOptionTLS: "TLS (465)",
|
||||||
|
"Ignore TLS Error": "Abaikan Kesalahan TLS",
|
||||||
|
"From Email": "Dari Surel",
|
||||||
|
"To Email": "Ke Surel",
|
||||||
|
smtpCC: "CC",
|
||||||
|
smtpBCC: "BCC",
|
||||||
|
"discord": "Discord",
|
||||||
|
"Discord Webhook URL": "Discord Webhook URL",
|
||||||
|
wayToGetDiscordURL: "Anda bisa mendapatkan ini dengan pergi ke Server Settings -> Integrations -> Create Webhook",
|
||||||
|
"Bot Display Name": "Nama Bot",
|
||||||
|
"Prefix Custom Message": "Awalan Pesan",
|
||||||
|
"Hello @everyone is...": "Halo {'@'}everyone is...",
|
||||||
|
"teams": "Microsoft Teams",
|
||||||
|
"Webhook URL": "Webhook URL",
|
||||||
|
wayToGetTeamsURL: "Anda dapat mempelajari cara membuat url webhook {0}.",
|
||||||
|
"signal": "Sinyal",
|
||||||
|
"Number": "Nomer",
|
||||||
|
"Recipients": "Penerima",
|
||||||
|
needSignalAPI: "Anda harus memiliki klien sinyal dengan REST API.",
|
||||||
|
wayToCheckSignalURL: "Anda dapat memeriksa url ini untuk melihat cara menyiapkannya:",
|
||||||
|
signalImportant: "PENTING: Anda tidak dapat mencampur grup dan nomor di penerima!",
|
||||||
|
"gotify": "Gotify",
|
||||||
|
"Application Token": "Token Aplikasi",
|
||||||
|
"Server URL": "URL Peladen",
|
||||||
|
"Priority": "Prioritas",
|
||||||
|
"slack": "Slack",
|
||||||
|
"Icon Emoji": "Ikon Emoji",
|
||||||
|
"Channel Name": "Nama Saluran",
|
||||||
|
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||||
|
aboutWebhooks: "Info lain tentang webhook: {0}",
|
||||||
|
aboutChannelName: "Masukan nama saluran di {0} Kolom Nama Saluran jika Anda ingin melewati saluran webhook. Contoh: #saluran-lain",
|
||||||
|
aboutKumaURL: "Jika Anda membiarkan bidang URL Uptime Kuma kosong, itu akan menjadi bawaan ke halaman Proyek Github.",
|
||||||
|
emojiCheatSheet: "Lembar contekan emoji: {0}",
|
||||||
|
"rocket.chat": "Rocket.chat",
|
||||||
|
pushover: "Pushover",
|
||||||
|
pushy: "Pushy",
|
||||||
|
octopush: "Octopush",
|
||||||
|
promosms: "PromoSMS",
|
||||||
|
lunasea: "LunaSea",
|
||||||
|
apprise: "Apprise (Mendukung 50+ layanan notifikasi)",
|
||||||
|
pushbullet: "Pushbullet",
|
||||||
|
line: "Line Messenger",
|
||||||
|
mattermost: "Mattermost",
|
||||||
|
"User Key": "Kunci pengguna",
|
||||||
|
"Device": "Perangkat",
|
||||||
|
"Message Title": "Judul Pesan",
|
||||||
|
"Notification Sound": "Suara Nofifikasi",
|
||||||
|
"More info on:": "Info lebih lanjut tentang: {0}",
|
||||||
|
pushoverDesc1: "Prioritas darurat (2) memiliki batas waktu bawaan 30 detik antara percobaan ulang dan akan kadaluwarsa setelah 1 jam.",
|
||||||
|
pushoverDesc2: "Jika Anda ingin mengirim pemberitahuan ke perangkat yang berbeda, isi kolom Perangkat.",
|
||||||
|
"SMS Type": "Tipe SMS",
|
||||||
|
octopushTypePremium: "Premium (Cepat - direkomendasikan untuk mengingatkan)",
|
||||||
|
octopushTypeLowCost: "Low Cost (Lambat, terkadang diblokir oleh operator)",
|
||||||
|
"Check octopush prices": "Cek harga octopush {0}.",
|
||||||
|
octopushPhoneNumber: "Nomer Telpon/HP (format internasional, contoh : +33612345678) ",
|
||||||
|
octopushSMSSender: "Nama Pengirim SMS : 3-11 karakter alfanumerik dan spasi (a-zA-Z0-9)",
|
||||||
|
"LunaSea Device ID": "LunaSea Device ID",
|
||||||
|
"Apprise URL": "Apprise URL",
|
||||||
|
"Example:": "Contoh: {0}",
|
||||||
|
"Read more:": "Baca lebih lajut: {0}",
|
||||||
|
"Status:": "Status: {0}",
|
||||||
|
"Read more": "Baca lebih lajut",
|
||||||
|
appriseInstalled: "Apprise diinstall.",
|
||||||
|
appriseNotInstalled: "Apprise tidak diinstall. {0}",
|
||||||
|
"Access Token": "Token Akses",
|
||||||
|
"Channel access token": "Token akses saluran",
|
||||||
|
"Line Developers Console": "Konsol Pengembang Line",
|
||||||
|
lineDevConsoleTo: "Konsol Pengembang Line - {0}",
|
||||||
|
"Basic Settings": "Pengaturan Dasar",
|
||||||
|
"User ID": "ID User",
|
||||||
|
"Messaging API": "Messaging API",
|
||||||
|
wayToGetLineChannelToken: "Pertama akses {0}, buat penyedia dan saluran (Messaging API), lalu Anda bisa mendapatkan token akses saluran dan id pengguna dari item menu yang disebutkan di atas.",
|
||||||
|
"Icon URL": "Icon URL",
|
||||||
|
aboutIconURL: "Anda dapat memberikan tautan ke gambar di \"Icon URL\" untuk mengganti gambar profil bawaan. Tidak akan digunakan jika Ikon Emoji diset.",
|
||||||
|
aboutMattermostChannelName: "Anda dapat mengganti saluran bawaan tujuan posting webhook dengan memasukkan nama saluran ke dalam Kolom \"Channel Name\". Ini perlu diaktifkan di pengaturan webhook Mattermost. contoh: #other-channel",
|
||||||
|
"matrix": "Matrix",
|
||||||
|
promosmsTypeEco: "SMS ECO - murah tapi lambat dan sering kelebihan beban. Terbatas hanya untuk penerima Polandia.",
|
||||||
|
promosmsTypeFlash: "SMS FLASH - Pesan akan otomatis muncul di perangkat penerima. Terbatas hanya untuk penerima Polandia.",
|
||||||
|
promosmsTypeFull: "SMS FULL - SMS tingkat premium, Anda dapat menggunakan Nama Pengirim Anda (Anda harus mendaftarkan nama terlebih dahulu). Dapat diAndalkan untuk peringatan.",
|
||||||
|
promosmsTypeSpeed: "SMS SPEED - Prioritas tertinggi dalam sistem. Sangat cepat dan dapat diAndalkan tetapi mahal (sekitar dua kali lipat dari harga SMS FULL).",
|
||||||
|
promosmsPhoneNumber: "Nomor telepon (untuk penerima Polandia Anda dapat melewati kode area)",
|
||||||
|
promosmsSMSSender: "Nama Pengirim SMS : Nama pra-registrasi atau salah satu bawaan: InfoSMS, Info SMS, MaxSMS, INFO, SMS",
|
||||||
|
"Feishu WebHookUrl": "Feishu WebHookUrl",
|
||||||
|
// End notification form
|
||||||
|
};
|
@ -0,0 +1,284 @@
|
|||||||
|
export default {
|
||||||
|
languageName: "Norsk",
|
||||||
|
checkEverySecond: "Sjekk hvert {0} sekund.",
|
||||||
|
retryCheckEverySecond: "Prøv igjen hvert {0} sekund.",
|
||||||
|
retriesDescription: "Maksimalt antall forsøk før tjenesten er merket som nede og et varsel sendes",
|
||||||
|
ignoreTLSError: "Ignorer TLS/SSL-feil for HTTPS-nettsteder",
|
||||||
|
upsideDownModeDescription: "Snu statusen opp ned. Hvis tjenesten er tilgjengelig, er den NED.",
|
||||||
|
maxRedirectDescription: "Maksimalt antall viderekoblinger å følge. Sett til 0 for å deaktivere viderekoblinger.",
|
||||||
|
acceptedStatusCodesDescription: "Velg statuskoder som anses som et vellykket svar.",
|
||||||
|
passwordNotMatchMsg: "Passordene stemmer ikke overens.",
|
||||||
|
notificationDescription: "Tilordne et varsel for å overvåkningen for å få det til å fungere.",
|
||||||
|
keywordDescription: "Søk etter nøkkelord i vanlig HTML eller JSON, og det er versalfølsom",
|
||||||
|
pauseDashboardHome: "Pause",
|
||||||
|
deleteMonitorMsg: "Er du sikker på at du vil slette denne overvåkningen?",
|
||||||
|
deleteNotificationMsg: "Er du sikker på at du vil slette dette varselet for alle overvåkningene?",
|
||||||
|
resoverserverDescription: "Cloudflare er standardserveren, kan du når som helst endre DNS-serveren.",
|
||||||
|
rrtypeDescription: "Velg RR-typen du vil overvåke",
|
||||||
|
pauseMonitorMsg: "Er du sikker på at du vil sette en pause?",
|
||||||
|
enableDefaultNotificationDescription: "For hver ny overvåkning vil denne varslingen være aktivert som standard. Du kan fortsatt deaktivere varselet separat for hver overvåkning.",
|
||||||
|
clearEventsMsg: "Er du sikker på at du vil slette alle hendelser for denne overvåkningen?",
|
||||||
|
clearHeartbeatsMsg: "Er du sikker på at du vil slette alle hjerteslag for denne overvåkningen?",
|
||||||
|
confirmClearStatisticsMsg: "Er du sikker på at du vil slette ALL statistikk?",
|
||||||
|
importHandleDescription: "Velg 'Hopp over eksisterende' hvis du vil hoppe over hver overvåkning eller varsel med samme navn. 'Overskriv' sletter alle eksisterende overvåkninger og varsler.",
|
||||||
|
confirmImportMsg: "Er du sikker på å importere sikkerhetskopien? Sørg for at du har valgt riktig importalternativ.",
|
||||||
|
twoFAVerifyLabel: "Skriv inn tokenet ditt for å bekrefte at 2FA fungerer",
|
||||||
|
tokenValidSettingsMsg: "Token er gyldig! Du kan nå lagre 2FA-innstillingene.",
|
||||||
|
confirmEnableTwoFAMsg: "Er du sikker på at du vil aktivere 2FA?",
|
||||||
|
confirmDisableTwoFAMsg: "Er du sikker på at du vil deaktivere 2FA?",
|
||||||
|
Settings: "Innstillinger",
|
||||||
|
Dashboard: "Dashboard",
|
||||||
|
"New Update": "Ny Oppdatering",
|
||||||
|
Language: "Språk",
|
||||||
|
Appearance: "Utseende",
|
||||||
|
Theme: "Tema",
|
||||||
|
General: "Generelt",
|
||||||
|
Version: "Versjon",
|
||||||
|
"Check Update On GitHub": "Sjekk oppdatering på GitHub",
|
||||||
|
List: "Liste",
|
||||||
|
Add: "Legg til",
|
||||||
|
"Add New Monitor": "Legg til ny overvåkning",
|
||||||
|
"Quick Stats": "Statistikk",
|
||||||
|
Up: "Oppe",
|
||||||
|
Down: "Nede",
|
||||||
|
Pending: "Avventer",
|
||||||
|
Unknown: "Ukjent",
|
||||||
|
Pause: "Pause",
|
||||||
|
Name: "Navn",
|
||||||
|
Status: "Status",
|
||||||
|
DateTime: "Dato tid",
|
||||||
|
Message: "Melding",
|
||||||
|
"No important events": "Ingen viktige hendelser",
|
||||||
|
Resume: "Fortsett",
|
||||||
|
Edit: "Endre",
|
||||||
|
Delete: "Slett",
|
||||||
|
Current: "Nåværende",
|
||||||
|
Uptime: "Oppetid",
|
||||||
|
"Cert Exp.": "Sertifikat utløper",
|
||||||
|
days: "dager",
|
||||||
|
day: "dag",
|
||||||
|
"-day": "-dag",
|
||||||
|
hour: "time",
|
||||||
|
"-hour": "-time",
|
||||||
|
Response: "Respons",
|
||||||
|
Ping: "Ping",
|
||||||
|
"Monitor Type": "Overvåkningstype",
|
||||||
|
Keyword: "Stikkord",
|
||||||
|
"Friendly Name": "Vennlig navn",
|
||||||
|
URL: "URL",
|
||||||
|
Hostname: "Vertsnavn",
|
||||||
|
Port: "Port",
|
||||||
|
"Heartbeat Interval": "Hjerteslagsintervall",
|
||||||
|
Retries: "Forsøk",
|
||||||
|
"Heartbeat Retry Interval": "Hjerteslagsforsøkintervall",
|
||||||
|
Advanced: "Avansert",
|
||||||
|
"Upside Down Mode": "Opp-ned-modus",
|
||||||
|
"Max. Redirects": "Maks. viderekoblinger",
|
||||||
|
"Accepted Status Codes": "Godkjente statuskoder",
|
||||||
|
Save: "Lagre",
|
||||||
|
Notifications: "Varsler",
|
||||||
|
"Not available, please setup.": "Ikke tilgjengelig, sett opp.",
|
||||||
|
"Setup Notification": "Sett opp varsel",
|
||||||
|
Light: "Lys",
|
||||||
|
Dark: "Mørk",
|
||||||
|
Auto: "Auto",
|
||||||
|
"Theme - Heartbeat Bar": "Theme - Heartbeat Bar",
|
||||||
|
Normal: "Normal",
|
||||||
|
Bottom: "Bunn",
|
||||||
|
None: "Ingen",
|
||||||
|
Timezone: "Tidssone",
|
||||||
|
"Search Engine Visibility": "Søkemotor synlighet",
|
||||||
|
"Allow indexing": "Tillat indeksering",
|
||||||
|
"Discourage search engines from indexing site": "Avskrekk søkemotorer fra å indeksere nettstedet",
|
||||||
|
"Change Password": "Endre passord",
|
||||||
|
"Current Password": "Nåværende passord",
|
||||||
|
"New Password": "Nytt passord",
|
||||||
|
"Repeat New Password": "Gjenta nytt passord",
|
||||||
|
"Update Password": "Oppdater passord",
|
||||||
|
"Disable Auth": "Deaktiver autentisering",
|
||||||
|
"Enable Auth": "Aktiver autentisering",
|
||||||
|
Logout: "Logg ut",
|
||||||
|
Leave: "Forlat",
|
||||||
|
"I understand, please disable": "Jeg forstår, deaktiver",
|
||||||
|
Confirm: "Bekreft",
|
||||||
|
Yes: "Ja",
|
||||||
|
No: "Nei",
|
||||||
|
Username: "Brukernavn",
|
||||||
|
Password: "Passord",
|
||||||
|
"Remember me": "Husk meg",
|
||||||
|
Login: "Logg inn",
|
||||||
|
"No Monitors, please": "Ingen overvåkning, vær så snill",
|
||||||
|
"add one": "legg til en",
|
||||||
|
"Notification Type": "Meldingstype",
|
||||||
|
Email: "E-post",
|
||||||
|
Test: "Test",
|
||||||
|
"Certificate Info": "Sertifikatinformasjon",
|
||||||
|
"Resolver Server": "DNS-server",
|
||||||
|
"Resource Record Type": "DNS-posttype",
|
||||||
|
"Last Result": "Siste resultat",
|
||||||
|
"Create your admin account": "Opprett en administratorkonto",
|
||||||
|
"Repeat Password": "Gjenta passord",
|
||||||
|
"Import Backup": "Importer sikkerhetskopi",
|
||||||
|
"Export Backup": "Eksporter sikkerhetskopi",
|
||||||
|
Export: "Eksporter",
|
||||||
|
Import: "Importer",
|
||||||
|
respTime: "Svartid (ms)",
|
||||||
|
notAvailableShort: "N/A",
|
||||||
|
"Default enabled": "Standard aktivert",
|
||||||
|
"Apply on all existing monitors": "Påfør på alle eksisterende overvåkninger",
|
||||||
|
Create: "Opprett",
|
||||||
|
"Clear Data": "Slett data",
|
||||||
|
Events: "Hendelser",
|
||||||
|
Heartbeats: "Hjerteslag",
|
||||||
|
"Auto Get": "Auto Get",
|
||||||
|
backupDescription: "Du kan sikkerhetskopiere alle overvåkninger og alle varsler til en JSON-fil.",
|
||||||
|
backupDescription2: "PS: Historikk og hendelsesdata er ikke inkludert.",
|
||||||
|
backupDescription3: "Følsomme data som varslingstokener er inkludert i eksportfilen. Vennligst oppbevar dem nøye.",
|
||||||
|
alertNoFile: "Velg en fil som skal importeres.",
|
||||||
|
alertWrongFileType: "Velg en JSON-fil.",
|
||||||
|
"Clear all statistics": "Fjern all statistikk",
|
||||||
|
"Skip existing": "Hopp over eksisterende",
|
||||||
|
Overwrite: "Overskriv",
|
||||||
|
Options: "Alternativer",
|
||||||
|
"Keep both": "Behold begge",
|
||||||
|
"Verify Token": "Bekreft token",
|
||||||
|
"Setup 2FA": "Konfigurer 2FA",
|
||||||
|
"Enable 2FA": "Aktiver 2FA",
|
||||||
|
"Disable 2FA": "Deaktiver 2FA",
|
||||||
|
"2FA Settings": "2FA Innstillinger",
|
||||||
|
"Two Factor Authentication": "To-faktor autentisering",
|
||||||
|
Active: "Aktiv",
|
||||||
|
Inactive: "Inaktiv",
|
||||||
|
Token: "Token",
|
||||||
|
"Show URI": "Vis URI",
|
||||||
|
Tags: "Etiketter",
|
||||||
|
"Add New below or Select...": "Legg til nytt nedenfor eller Velg ...",
|
||||||
|
"Tag with this name already exist.": "Etikett med dette navnet eksisterer allerede.",
|
||||||
|
"Tag with this value already exist.": "Etikett med denne verdien finnes allerede.",
|
||||||
|
color: "farge",
|
||||||
|
"value (optional)": "verdi (valgfritt)",
|
||||||
|
Gray: "Grå",
|
||||||
|
Red: "Rød",
|
||||||
|
Orange: "Oransje",
|
||||||
|
Green: "Grønn",
|
||||||
|
Blue: "Blå",
|
||||||
|
Indigo: "Indigo",
|
||||||
|
Purple: "Lilla",
|
||||||
|
Pink: "Rosa",
|
||||||
|
"Search...": "Søk...",
|
||||||
|
"Avg. Ping": "Gj.sn. Ping",
|
||||||
|
"Avg. Response": "Gj.sn. Respons",
|
||||||
|
"Entry Page": "Oppføringsside",
|
||||||
|
statusPageNothing: "Ingenting her, vennligst legg til en gruppe eller en overvåkning.",
|
||||||
|
"No Services": "Ingen tjenester",
|
||||||
|
"All Systems Operational": "Alle systemer i drift",
|
||||||
|
"Partially Degraded Service": "Delvis degradert drift",
|
||||||
|
"Degraded Service": "Degradert drift",
|
||||||
|
"Add Group": "Legg til gruppe",
|
||||||
|
"Add a monitor": "Legg til en overvåkning",
|
||||||
|
"Edit Status Page": "Rediger statusside",
|
||||||
|
"Go to Dashboard": "Gå til Dashboard",
|
||||||
|
"Status Page": "Statusside",
|
||||||
|
// Start notification form
|
||||||
|
defaultNotificationName: "Min {notification} varsling ({number})",
|
||||||
|
here: "here",
|
||||||
|
"Required": "Obligatorisk",
|
||||||
|
"telegram": "Telegram",
|
||||||
|
"Bot Token": "Bot Token",
|
||||||
|
"You can get a token from": "Du kan få et token fra",
|
||||||
|
"Chat ID": "Chat ID",
|
||||||
|
supportTelegramChatID: "Support Direct Chat / Group / Channel's Chat ID",
|
||||||
|
wayToGetTelegramChatID: "Du kan få chat-ID-en din ved å sende meldingen til boten og gå til denne nettadressen for å se chat_id:",
|
||||||
|
"YOUR BOT TOKEN HERE": "DITT BOT TOKEN HER",
|
||||||
|
chatIDNotFound: "Chat-ID ble ikke funnet. Send en melding til denne boten først",
|
||||||
|
"webhook": "Webhook",
|
||||||
|
"Post URL": "Post URL",
|
||||||
|
"Content Type": "Content Type",
|
||||||
|
webhookJsonDesc: "{0} er bra for alle moderne HTTP-servere som express.js",
|
||||||
|
webhookFormDataDesc: "{multipart} er bra for PHP, du trenger bare å analysere JSON etter {decodeFunction}",
|
||||||
|
"smtp": "E-post (SMTP)",
|
||||||
|
secureOptionNone: "None / STARTTLS (25, 587)",
|
||||||
|
secureOptionTLS: "TLS (465)",
|
||||||
|
"Ignore TLS Error": "Ignorer TLS feilmelding",
|
||||||
|
"From Email": "Fra E-post",
|
||||||
|
"To Email": "Til E-post",
|
||||||
|
smtpCC: "CC",
|
||||||
|
smtpBCC: "BCC",
|
||||||
|
"discord": "Discord",
|
||||||
|
"Discord Webhook URL": "Discord Webhook URL",
|
||||||
|
wayToGetDiscordURL: "Du kan få dette ved å gå til Serverinnstillinger -> Integrasjoner -> Webhooks -> Ny webhook",
|
||||||
|
"Bot Display Name": "Bot Visningsnavn",
|
||||||
|
"Prefix Custom Message": "Prefiks tilpasset melding",
|
||||||
|
"Hello @everyone is...": "Hei {'@'}everyone det er...",
|
||||||
|
"teams": "Microsoft Teams",
|
||||||
|
"Webhook URL": "Webhook URL",
|
||||||
|
wayToGetTeamsURL: "Du kan lære hvordan du oppretter en webhook-URL {0}.",
|
||||||
|
"signal": "Signal",
|
||||||
|
"Number": "Nummer",
|
||||||
|
"Recipients": "Mottakere",
|
||||||
|
needSignalAPI: "Du må ha en Signal-klient med REST API.",
|
||||||
|
wayToCheckSignalURL: "Du kan sjekke denne nettadressen for å se hvordan du konfigurerer en:",
|
||||||
|
signalImportant: "VIKTIG: Du kan ikke blande grupper og nummere i mottakere!",
|
||||||
|
"gotify": "Gotify",
|
||||||
|
"Application Token": "Application Token",
|
||||||
|
"Server URL": "Server URL",
|
||||||
|
"Priority": "Prioritet",
|
||||||
|
"slack": "Slack",
|
||||||
|
"Icon Emoji": "Icon Emoji",
|
||||||
|
"Channel Name": "Kanal navn",
|
||||||
|
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||||
|
aboutWebhooks: "Mer informasjon om webhooks på: {0}",
|
||||||
|
aboutChannelName: "Skriv inn kanalnavnet på {0} Kanalnavn-feltet hvis du vil omgå webhook-kanalen. Eks: #other-channel",
|
||||||
|
aboutKumaURL: "Hvis du lar Uptime Kuma URL feltet være blank, den blir som standard til Github-siden for dette prosjektet.",
|
||||||
|
emojiCheatSheet: "Emoji cheat sheet: {0}",
|
||||||
|
"rocket.chat": "Rocket.chat",
|
||||||
|
pushover: "Pushover",
|
||||||
|
pushy: "Pushy",
|
||||||
|
octopush: "Octopush",
|
||||||
|
promosms: "PromoSMS",
|
||||||
|
lunasea: "LunaSea",
|
||||||
|
apprise: "Apprise (Support 50+ Notification services)",
|
||||||
|
pushbullet: "Pushbullet",
|
||||||
|
line: "Line Messenger",
|
||||||
|
mattermost: "Mattermost",
|
||||||
|
"User Key": "User Key",
|
||||||
|
"Device": "Device",
|
||||||
|
"Message Title": "Message Title",
|
||||||
|
"Notification Sound": "Notification Sound",
|
||||||
|
"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 Type",
|
||||||
|
octopushTypePremium: "Premium (Fast - recommended for alerting)",
|
||||||
|
octopushTypeLowCost: "Low Cost (Slow, sometimes blocked by operator)",
|
||||||
|
"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:": "Example: {0}",
|
||||||
|
"Read more:": "Read more: {0}",
|
||||||
|
"Status:": "Status: {0}",
|
||||||
|
"Read more": "Read more",
|
||||||
|
appriseInstalled: "Apprise is installed.",
|
||||||
|
appriseNotInstalled: "Apprise is not installed. {0}",
|
||||||
|
"Access Token": "Access Token",
|
||||||
|
"Channel access token": "Channel access token",
|
||||||
|
"Line Developers Console": "Line Developers Console",
|
||||||
|
lineDevConsoleTo: "Line Developers Console - {0}",
|
||||||
|
"Basic Settings": "Basic Settings",
|
||||||
|
"User ID": "User 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": "Icon 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 webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in 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",
|
||||||
|
// End notification form
|
||||||
|
};
|
@ -1,118 +1,118 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
// Common Util for frontend and backend
|
// Common Util for frontend and backend
|
||||||
//
|
//
|
||||||
// DOT NOT MODIFY util.js!
|
// DOT NOT MODIFY util.js!
|
||||||
// Need to run "tsc" to compile if there are any changes.
|
// Need to run "tsc" to compile if there are any changes.
|
||||||
//
|
//
|
||||||
// Backend uses the compiled file util.js
|
// Backend uses the compiled file util.js
|
||||||
// Frontend uses util.ts
|
// Frontend uses util.ts
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
|
exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
|
||||||
const _dayjs = require("dayjs");
|
const _dayjs = require("dayjs");
|
||||||
const dayjs = _dayjs;
|
const dayjs = _dayjs;
|
||||||
exports.isDev = process.env.NODE_ENV === "development";
|
exports.isDev = process.env.NODE_ENV === "development";
|
||||||
exports.appName = "Uptime Kuma";
|
exports.appName = "Uptime Kuma";
|
||||||
exports.DOWN = 0;
|
exports.DOWN = 0;
|
||||||
exports.UP = 1;
|
exports.UP = 1;
|
||||||
exports.PENDING = 2;
|
exports.PENDING = 2;
|
||||||
exports.STATUS_PAGE_ALL_DOWN = 0;
|
exports.STATUS_PAGE_ALL_DOWN = 0;
|
||||||
exports.STATUS_PAGE_ALL_UP = 1;
|
exports.STATUS_PAGE_ALL_UP = 1;
|
||||||
exports.STATUS_PAGE_PARTIAL_DOWN = 2;
|
exports.STATUS_PAGE_PARTIAL_DOWN = 2;
|
||||||
function flipStatus(s) {
|
function flipStatus(s) {
|
||||||
if (s === exports.UP) {
|
if (s === exports.UP) {
|
||||||
return exports.DOWN;
|
return exports.DOWN;
|
||||||
}
|
}
|
||||||
if (s === exports.DOWN) {
|
if (s === exports.DOWN) {
|
||||||
return exports.UP;
|
return exports.UP;
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
exports.flipStatus = flipStatus;
|
exports.flipStatus = flipStatus;
|
||||||
function sleep(ms) {
|
function sleep(ms) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
exports.sleep = sleep;
|
exports.sleep = sleep;
|
||||||
/**
|
/**
|
||||||
* PHP's ucfirst
|
* PHP's ucfirst
|
||||||
* @param str
|
* @param str
|
||||||
*/
|
*/
|
||||||
function ucfirst(str) {
|
function ucfirst(str) {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
const firstLetter = str.substr(0, 1);
|
const firstLetter = str.substr(0, 1);
|
||||||
return firstLetter.toUpperCase() + str.substr(1);
|
return firstLetter.toUpperCase() + str.substr(1);
|
||||||
}
|
}
|
||||||
exports.ucfirst = ucfirst;
|
exports.ucfirst = ucfirst;
|
||||||
function debug(msg) {
|
function debug(msg) {
|
||||||
if (exports.isDev) {
|
if (exports.isDev) {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.debug = debug;
|
exports.debug = debug;
|
||||||
function polyfill() {
|
function polyfill() {
|
||||||
/**
|
/**
|
||||||
* String.prototype.replaceAll() polyfill
|
* String.prototype.replaceAll() polyfill
|
||||||
* https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/
|
* https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/
|
||||||
* @author Chris Ferdinandi
|
* @author Chris Ferdinandi
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
if (!String.prototype.replaceAll) {
|
if (!String.prototype.replaceAll) {
|
||||||
String.prototype.replaceAll = function (str, newStr) {
|
String.prototype.replaceAll = function (str, newStr) {
|
||||||
// If a regex pattern
|
// If a regex pattern
|
||||||
if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") {
|
if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") {
|
||||||
return this.replace(str, newStr);
|
return this.replace(str, newStr);
|
||||||
}
|
}
|
||||||
// If a string
|
// If a string
|
||||||
return this.replace(new RegExp(str, "g"), newStr);
|
return this.replace(new RegExp(str, "g"), newStr);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.polyfill = polyfill;
|
exports.polyfill = polyfill;
|
||||||
class TimeLogger {
|
class TimeLogger {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.startTime = dayjs().valueOf();
|
this.startTime = dayjs().valueOf();
|
||||||
}
|
}
|
||||||
print(name) {
|
print(name) {
|
||||||
if (exports.isDev && process.env.TIMELOGGER === "1") {
|
if (exports.isDev && process.env.TIMELOGGER === "1") {
|
||||||
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
|
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.TimeLogger = TimeLogger;
|
exports.TimeLogger = TimeLogger;
|
||||||
/**
|
/**
|
||||||
* Returns a random number between min (inclusive) and max (exclusive)
|
* Returns a random number between min (inclusive) and max (exclusive)
|
||||||
*/
|
*/
|
||||||
function getRandomArbitrary(min, max) {
|
function getRandomArbitrary(min, max) {
|
||||||
return Math.random() * (max - min) + min;
|
return Math.random() * (max - min) + min;
|
||||||
}
|
}
|
||||||
exports.getRandomArbitrary = getRandomArbitrary;
|
exports.getRandomArbitrary = getRandomArbitrary;
|
||||||
/**
|
/**
|
||||||
* From: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
|
* From: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
|
||||||
*
|
*
|
||||||
* Returns a random integer between min (inclusive) and max (inclusive).
|
* Returns a random integer between min (inclusive) and max (inclusive).
|
||||||
* The value is no lower than min (or the next integer greater than min
|
* The value is no lower than min (or the next integer greater than min
|
||||||
* if min isn't an integer) and no greater than max (or the next integer
|
* if min isn't an integer) and no greater than max (or the next integer
|
||||||
* lower than max if max isn't an integer).
|
* lower than max if max isn't an integer).
|
||||||
* Using Math.round() will give you a non-uniform distribution!
|
* Using Math.round() will give you a non-uniform distribution!
|
||||||
*/
|
*/
|
||||||
function getRandomInt(min, max) {
|
function getRandomInt(min, max) {
|
||||||
min = Math.ceil(min);
|
min = Math.ceil(min);
|
||||||
max = Math.floor(max);
|
max = Math.floor(max);
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
}
|
}
|
||||||
exports.getRandomInt = getRandomInt;
|
exports.getRandomInt = getRandomInt;
|
||||||
function genSecret(length = 64) {
|
function genSecret(length = 64) {
|
||||||
let secret = "";
|
let secret = "";
|
||||||
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
let charsLength = chars.length;
|
let charsLength = chars.length;
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
secret += chars.charAt(Math.floor(Math.random() * charsLength));
|
secret += chars.charAt(Math.floor(Math.random() * charsLength));
|
||||||
}
|
}
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
exports.genSecret = genSecret;
|
exports.genSecret = genSecret;
|
||||||
function getMonitorRelativeURL(id) {
|
function getMonitorRelativeURL(id) {
|
||||||
return "/dashboard/" + id;
|
return "/dashboard/" + id;
|
||||||
}
|
}
|
||||||
exports.getMonitorRelativeURL = getMonitorRelativeURL;
|
exports.getMonitorRelativeURL = getMonitorRelativeURL;
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
const { genSecret, sleep } = require("../src/util");
|
||||||
|
|
||||||
|
describe("Test genSecret", () => {
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be correct length", () => {
|
||||||
|
let secret = genSecret(-1);
|
||||||
|
expect(secret).toEqual("");
|
||||||
|
|
||||||
|
secret = genSecret(0);
|
||||||
|
expect(secret).toEqual("");
|
||||||
|
|
||||||
|
secret = genSecret(1);
|
||||||
|
expect(secret.length).toEqual(1);
|
||||||
|
|
||||||
|
secret = genSecret(2);
|
||||||
|
expect(secret.length).toEqual(2);
|
||||||
|
|
||||||
|
secret = genSecret(64);
|
||||||
|
expect(secret.length).toEqual(64);
|
||||||
|
|
||||||
|
secret = genSecret(9000);
|
||||||
|
expect(secret.length).toEqual(9000);
|
||||||
|
|
||||||
|
secret = genSecret(90000);
|
||||||
|
expect(secret.length).toEqual(90000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should contain first and last possible chars", () => {
|
||||||
|
let secret = genSecret(90000);
|
||||||
|
expect(secret).toContain("A");
|
||||||
|
expect(secret).toContain("9");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test reset-password", () => {
|
||||||
|
it("should able to run", async () => {
|
||||||
|
await require("../extra/reset-password").main();
|
||||||
|
}, 120000);
|
||||||
|
});
|
Loading…
Reference in new issue