Louis Lam
3 years ago
58 changed files with 2215 additions and 560 deletions
@ -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