Merge branch 'louislam:master' into master

This commit is contained in:
Ioma Taani 2021-10-16 10:29:28 +02:00 committed by GitHub
commit 90bcec5204
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 315 additions and 140 deletions

View file

@ -9,8 +9,8 @@ currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 1.7.X | :white_check_mark: |
| < 1.7 | |
| 1.8.X | :white_check_mark: |
| <= 1.7.X | ❌ |
### Upgradable Docker Tags

View file

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

View file

@ -12,50 +12,59 @@ const rl = readline.createInterface({
output: process.stdout
});
(async () => {
const main = async () => {
Database.init(args);
await Database.connect();
try {
const user = await R.findOne("user");
if (! user) {
throw new Error("user not found, have you installed?");
}
console.log("Found user: " + user.username);
while (true) {
let password = await question("New Password: ");
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await user.resetPassword(password);
// Reset all sessions by reset jwt secret
await initJWTSecret();
rl.close();
break;
} else {
console.log("Passwords do not match, please try again.");
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
if (!process.env.TEST_BACKEND) {
const user = await R.findOne("user");
if (! user) {
throw new Error("user not found, have you installed?");
}
}
console.log("Password reset successfully.");
console.log("Found user: " + user.username);
while (true) {
let password = await question("New Password: ");
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await user.resetPassword(password);
// Reset all sessions by reset jwt secret
await initJWTSecret();
break;
} else {
console.log("Passwords do not match, please try again.");
}
}
console.log("Password reset successfully.");
}
} catch (e) {
console.error("Error: " + e.message);
}
await Database.close();
rl.close();
console.log("Finished. You should restart the Uptime Kuma server.")
})();
console.log("Finished.");
};
function question(question) {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer);
})
});
});
}
if (!process.env.TEST_BACKEND) {
main();
}
module.exports = {
main,
};

7
server/config.js Normal file
View file

@ -0,0 +1,7 @@
const args = require("args-parser")(process.argv);
const demoMode = args["demo"] || false;
module.exports = {
args,
demoMode
};

View file

@ -49,6 +49,7 @@ class Database {
"patch-incident-table.sql": true,
"patch-group-table.sql": true,
"patch-monitor-push_token.sql": true,
"patch-http-monitor-method-body-and-headers.sql": true,
}
/**

View file

@ -11,7 +11,7 @@ const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalCli
const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification");
const { demoMode } = require("../server");
const { demoMode } = require("../config");
const version = require("../../package.json").version;
const apicache = require("../modules/apicache");
@ -55,6 +55,9 @@ class Monitor extends BeanModel {
id: this.id,
name: this.name,
url: this.url,
method: this.method,
body: this.body,
headers: this.headers,
hostname: this.hostname,
port: this.port,
maxretries: this.maxretries,
@ -138,11 +141,15 @@ class Monitor extends BeanModel {
// Do not do any queries/high loading things before the "bean.ping"
let startTime = dayjs().valueOf();
let res = await axios.get(this.url, {
const options = {
url: this.url,
method: (this.method || "get").toLowerCase(),
...(this.body ? { data: JSON.parse(this.body) } : {}),
timeout: this.interval * 1000 * 0.8,
headers: {
"Accept": "*/*",
"User-Agent": "Uptime-Kuma/" + version,
...(this.headers ? JSON.parse(this.headers) : {}),
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
@ -152,7 +159,8 @@ class Monitor extends BeanModel {
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
},
});
};
let res = await axios.request(options);
bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime;
@ -172,6 +180,10 @@ class Monitor extends BeanModel {
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
}
if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) {
console.log(res.data);
}
if (this.type === "http") {
bean.status = UP;
} else {

View file

@ -1,6 +1,7 @@
console.log("Welcome to Uptime Kuma");
const args = require("args-parser")(process.argv);
const { sleep, debug, getRandomInt, genSecret } = require("../src/util");
const config = require("./config");
debug(args);
@ -8,10 +9,6 @@ if (! process.env.NODE_ENV) {
process.env.NODE_ENV = "production";
}
// Demo Mode?
const demoMode = args["demo"] || false;
exports.demoMode = demoMode;
console.log("Node Env: " + process.env.NODE_ENV);
console.log("Importing Node libraries");
@ -78,13 +75,19 @@ const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.p
const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined;
const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined;
// 2FA / notp verification defaults
const twofa_verification_opts = {
"window": 1,
"time": 30
}
/**
* Run unit test after the server is ready
* @type {boolean}
*/
const testMode = !!args["test"] || false;
if (demoMode) {
if (config.demoMode) {
console.log("==== Demo Mode ====");
}
@ -275,7 +278,7 @@ exports.entryPage = "dashboard";
}
if (data.token) {
let verify = notp.totp.verify(data.token, user.twofa_secret);
let verify = notp.totp.verify(data.token, user.twofa_secret, twofa_verification_opts);
if (verify && verify.delta == 0) {
callback({
@ -393,7 +396,7 @@ exports.entryPage = "dashboard";
socket.userID,
]);
let verify = notp.totp.verify(token, user.twofa_secret);
let verify = notp.totp.verify(token, user.twofa_secret, twofa_verification_opts);
if (verify && verify.delta == 0) {
callback({
@ -519,6 +522,9 @@ exports.entryPage = "dashboard";
bean.name = monitor.name;
bean.type = monitor.type;
bean.url = monitor.url;
bean.method = monitor.method;
bean.body = monitor.body;
bean.headers = monitor.headers;
bean.interval = monitor.interval;
bean.retryInterval = monitor.retryInterval;
bean.hostname = monitor.hostname;
@ -1044,6 +1050,9 @@ exports.entryPage = "dashboard";
name: monitorListData[i].name,
type: monitorListData[i].type,
url: monitorListData[i].url,
method: monitorListData[i].method || "GET",
body: monitorListData[i].body,
headers: monitorListData[i].headers,
interval: monitorListData[i].interval,
retryInterval: retryInterval,
hostname: monitorListData[i].hostname,

View file

@ -327,7 +327,7 @@ exports.startUnitTest = async () => {
*/
exports.convertToUTF8 = (body) => {
const guessEncoding = chardet.detect(body);
debug("Guess Encoding: " + guessEncoding);
//debug("Guess Encoding: " + guessEncoding);
const str = iconv.decode(body, guessEncoding);
return str.toString();
};

View file

@ -14,6 +14,10 @@ h2 {
font-size: 26px;
}
textarea.form-control {
border-radius: 19px;
}
::-webkit-scrollbar {
width: 10px;
}

View file

@ -21,7 +21,7 @@
}
.multiselect__tag {
border-radius: 50rem;
border-radius: $border-radius;
margin-bottom: 0;
padding: 6px 26px 6px 10px;
background: $primary !important;

View file

@ -186,7 +186,7 @@ export default {
.beat {
display: inline-block;
background-color: $primary;
border-radius: 50rem;
border-radius: $border-radius;
&.empty {
background-color: aliceblue;

View file

@ -283,4 +283,10 @@ export default {
promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
"Feishu WebHookUrl": "Feishu WebHookUrl",
// End notification form
Method: "Method",
Body: "Body",
Headers: "Headers",
PushUrl: "Push URL",
HeadersInvalidFormat: "The request headers are not valid JSON: ",
BodyInvalidFormat: "The request body is not valid JSON: ",
};

View file

@ -1,44 +1,44 @@
export default {
languageName: "Indonesia",
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 respons yang berhasil.",
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 default, 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 default. 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?",
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 valid! Anda sekarang dapat menyimpan pengaturan 2FA.",
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: "Dashboard",
"New Update": "Update Baru",
Dashboard: "Dasbor",
"New Update": "Pembaruan Baru",
Language: "Bahasa",
Appearance: "Tampilan",
Theme: "Tema",
General: "General",
General: "Umum",
Version: "Versi",
"Check Update On GitHub": "Cek Update di GitHub",
List: "List",
"Check Update On GitHub": "Cek Pembaruan di GitHub",
List: "Daftar",
Add: "Tambah",
"Add New Monitor": "Tambah Monitor Baru",
"Quick Stats": "Statistik Cepat",
"Quick Stats": "Statistik",
Up: "Aktif",
Down: "Tidak Aktif",
Pending: "Tertunda",
@ -48,9 +48,9 @@ export default {
Status: "Status",
DateTime: "Tanggal Waktu",
Message: "Pesan",
"No important events": "Tidak ada Event penting",
Resume: "Melanjutkan",
Edit: "Rubah",
"No important events": "Tidak ada peristiwa penting",
Resume: "Lanjut",
Edit: "Ubah",
Delete: "Hapus",
Current: "Saat ini",
Uptime: "Waktu aktif",
@ -60,20 +60,20 @@ export default {
"-day": "-hari",
hour: "Jam",
"-hour": "-Jam",
Response: "Respon",
Response: "Tanggapan",
Ping: "Ping",
"Monitor Type": "Tipe Monitor",
Keyword: "Keyword",
"Friendly Name": "Friendly Name",
"Friendly Name": "Nama yang Ramah",
URL: "URL",
Hostname: "Hostname",
Port: "Port",
"Heartbeat Interval": "Interval Heartbeat ",
Retries: "Retries",
"Heartbeat Retry Interval": "Interval Heartbeat Mencoba kembali ",
Advanced: "Advanced",
"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 Redirect/Pengalihan",
"Max. Redirects": "Maksimal Pengalihan",
"Accepted Status Codes": "Kode Status yang Diterima",
Save: "Simpan",
Notifications: "Notifikasi",
@ -81,25 +81,25 @@ export default {
"Setup Notification": "Setel Notifikasi",
Light: "Terang",
Dark: "Gelap",
Auto: "Automatis",
"Theme - Heartbeat Bar": "Theme - Heartbeat Bar",
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 site",
"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 auth",
"Enable Auth": "Aktifkan Auth",
"Disable Auth": "Nonaktifkan Autentikasi",
"Enable Auth": "Aktifkan Autentikasi",
Logout: "Keluar",
Leave: "Pergi",
"I understand, please disable": "Saya mengerti, silahkan dinonaktifkan",
"I understand, please disable": "Saya mengerti, silakan dinonaktifkan",
Confirm: "Konfirmasi",
Yes: "Ya",
No: "Tidak",
@ -107,35 +107,35 @@ export default {
Password: "Sandi",
"Remember me": "Ingat saya",
Login: "Masuk",
"No Monitors, please": "Tidak ada monitor, silahkan",
"add one": "tambah baru",
"No Monitors, please": "Tidak ada monitor, silakan",
"add one": "tambahkan satu",
"Notification Type": "Tipe Notifikasi",
Email: "Email",
Test: "Test",
"Certificate Info": "Info Sertifikasi ",
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",
"Create your admin account": "Buat admin akun Anda",
"Repeat Password": "Ulangi Sandi",
"Import Backup": "Impor Backup",
"Export Backup": "Expor Backup",
"Import Backup": "Impor Cadangan",
"Export Backup": "Expor Cadangan",
Export: "Expor",
Import: "Impor",
respTime: "Tanggapan. Waktu (milidetik)",
notAvailableShort: "N/A",
"Default enabled": "Default diaktifkan",
"Default enabled": "Bawaan diaktifkan",
"Apply on all existing monitors": "Terapkan pada semua monitor yang ada",
Create: "Buat",
"Clear Data": "Bersihkan Data",
Events: "Event",
Events: "Peristiwa",
Heartbeats: "Heartbeats",
"Auto Get": "Auto Get",
backupDescription: "Anda dapat mencadangkan semua monitor dan semua notifikasi ke dalam file JSON.",
backupDescription2: "Catatan: Data sejarah dan event tidak disertakan.",
backupDescription3: "Data sensitif seperti notifikasi token disertakan dalam file ekspor, harap simpan dengan hati-hati.",
alertNoFile: "Silakan pilih file untuk diimpor.",
alertWrongFileType: "Silakan pilih file JSON .",
"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",
@ -145,29 +145,29 @@ export default {
"Setup 2FA": "Pengaturan 2FA",
"Enable 2FA": "Aktifkan 2FA",
"Disable 2FA": "Nonaktifkan 2FA",
"2FA Settings": "Settings 2FA",
"Two Factor Authentication": "Otentikasi Dua Faktor",
"2FA Settings": "Pengaturan 2FA",
"Two Factor Authentication": "Autentikasi Dua Faktor",
Active: "Aktif",
Inactive: "Tidak Aktif",
Token: "Token",
"Show URI": "Lihat URI",
Tags: "Tag",
Tags: "Tanda",
"Add New below or Select...": "Tambahkan Baru di bawah atau Pilih...",
"Tag with this name already exist.": "Tag dengan nama ini sudah ada.",
"Tag with this value already exist.": "Tag dengan nilai ini sudah ada.",
"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",
Gray: "Abu-abu",
Red: "Merah",
Orange: "Oranye",
Orange: "Jingga",
Green: "Hijau",
Blue: "Biru",
Indigo: "Indigo",
Indigo: "Biru Tua",
Purple: "Ungu",
Pink: "Merah Muda",
"Search...": "Cari...",
"Avg. Ping": "Rata-rata. Ping",
"Avg. Response": "Rata-rata. Respon",
"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",
@ -177,7 +177,7 @@ export default {
"Add Group": "Tambah Grup",
"Add a monitor": "Tambah monitor",
"Edit Status Page": "Edit Halaman Status",
"Go to Dashboard": "Lihat Dashboard",
"Go to Dashboard": "Pergi ke Dasbor",
"Status Page": "Halaman Status",
// Start notification form
defaultNotificationName: "{notification} saya Peringatan ({number})",
@ -194,22 +194,22 @@ export default {
"webhook": "Webhook",
"Post URL": "Post URL",
"Content Type": "Tipe konten",
webhookJsonDesc: "{0} bagus untuk server http modern seperti express.js",
webhookJsonDesc: "{0} bagus untuk peladen http modern seperti express.js",
webhookFormDataDesc: "{multipart} bagus untuk PHP, Anda hanya perlu mengurai json dengan {decodeFunction}",
"smtp": "Email (SMTP)",
"smtp": "Surel (SMTP)",
secureOptionNone: "None / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Ignore TLS Error",
"From Email": "From Email",
"To Email": "To Email",
"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": "Prefix Pesan",
"Hello @everyone is...": "Hallo {'@'}everyone is...",
"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}.",
@ -221,16 +221,16 @@ export default {
signalImportant: "PENTING: Anda tidak dapat mencampur grup dan nomor di penerima!",
"gotify": "Gotify",
"Application Token": "Token Aplikasi",
"Server URL": "Server URL",
"Server URL": "URL Peladen",
"Priority": "Prioritas",
"slack": "Slack",
"Icon Emoji": "Icon Emoji",
"Channel Name": "Nama Channel",
"Icon Emoji": "Ikon Emoji",
"Channel Name": "Nama Saluran",
"Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "Info lain tentang webhook: {0}",
aboutChannelName: "Masukan nama channel di {0} Kolom Nama Channel jika Anda ingin melewati channel webhook. Contoh: #other-channel",
aboutKumaURL: "Jika Anda membiarkan bidang URL Uptime Kuma kosong, itu akan menjadi default ke halaman Project Github.",
emojiCheatSheet: "Emoji cheat sheet: {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",
@ -246,7 +246,7 @@ export default {
"Message Title": "Judul Pesan",
"Notification Sound": "Suara Nofifikasi",
"More info on:": "Info lebih lanjut tentang: {0}",
pushoverDesc1: "Prioritas darurat (2) memiliki batas waktu default 30 detik antara percobaan ulang dan akan kadaluwarsa setelah 1 jam.",
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)",
@ -262,24 +262,24 @@ export default {
"Read more": "Baca lebih lajut",
appriseInstalled: "Apprise diinstall.",
appriseNotInstalled: "Apprise tidak diinstall. {0}",
"Access Token": "Access Token",
"Channel access token": "Channel access token",
"Line Developers Console": "Line Developers Console",
lineDevConsoleTo: "Line Developers Console - {0}",
"Basic Settings": "Pengaturan dasar",
"User ID": "User ID",
"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 default. Tidak akan digunakan jika Ikon Emoji diset.",
aboutMattermostChannelName: "Anda dapat mengganti channel default tujuan posting webhook dengan memasukkan nama channel ke dalam Kolom \"Channel Name\". Ini perlu diaktifkan di pengaturan webhook Mattermost. contoh: #other-channel",
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).",
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 default: InfoSMS, Info SMS, MaxSMS, INFO, SMS",
promosmsSMSSender: "Nama Pengirim SMS : Nama pra-registrasi atau salah satu bawaan: InfoSMS, Info SMS, MaxSMS, INFO, SMS",
"Feishu WebHookUrl": "Feishu WebHookUrl",
// End notification form
};

View file

@ -198,4 +198,12 @@ export default {
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
Method: "Methode",
Body: "Body",
Headers: "Headers",
PushUrl: "Push URL",
HeadersInvalidFormat: "The request headers is geen geldige JSON: ",
BodyInvalidFormat: "De request body is geen geldige JSON: ",
BodyPlaceholder: "&lbrace;\n\t\"id\": 124357,\n\t\"gebruikersnaam\": \"admin\",\n\t\"wachtwoord\": \"mijnAdminWachtwoord\"\n&rbrace;",
HeadersPlaceholder: "&lbrace;\n\t\"Authorization\": \"Bearer abc123\",\n\t\"Content-Type\": \"application/json\"\n&rbrace;",
};

View file

@ -46,7 +46,7 @@
<!-- Push URL -->
<div v-if="monitor.type === 'push' " class="my-3">
<label for="push-url" class="form-label">{{ $t("Push URL") }}</label>
<label for="push-url" class="form-label">{{ $t("PushUrl") }}</label>
<CopyableInput id="push-url" v-model="pushURL" type="url" disabled="disabled" />
<div class="form-text">
You should call this url every {{ monitor.interval }} seconds.<br />
@ -196,6 +196,7 @@
<div class="col-md-6">
<div v-if="$root.isMobile" class="mt-3" />
<!-- Notifications -->
<h2 class="mb-2">{{ $t("Notifications") }}</h2>
<p v-if="$root.notificationList.length === 0">
{{ $t("Not available, please setup.") }}
@ -215,6 +216,51 @@
<button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
{{ $t("Setup Notification") }}
</button>
<!-- HTTP Options -->
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
<h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2>
<!-- Method -->
<div class="my-3">
<label for="method" class="form-label">{{ $t("Method") }}</label>
<select id="method" v-model="monitor.method" class="form-select">
<option value="GET">
GET
</option>
<option value="POST">
POST
</option>
<option value="PUT">
PUT
</option>
<option value="PATCH">
PATCH
</option>
<option value="DELETE">
DELETE
</option>
<option value="HEAD">
HEAD
</option>
<option value="OPTIONS">
OPTIONS
</option>
</select>
</div>
<!-- Body -->
<div class="my-3">
<label for="body" class="form-label">{{ $t("Body") }}</label>
<textarea id="body" v-model="monitor.body" class="form-control" :placeholder="bodyPlaceholder"></textarea>
</div>
<!-- Headers -->
<div class="my-3">
<label for="headers" class="form-label">{{ $t("Headers") }}</label>
<textarea id="headers" v-model="monitor.headers" class="form-control" :placeholder="headersPlaceholder"></textarea>
</div>
</template>
</div>
</div>
</div>
@ -286,6 +332,14 @@ export default {
pushURL() {
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?msg=OK&ping=";
},
bodyPlaceholder() {
return this.decodeHtml("&lbrace;\n\t\"id\": 124357,\n\t\"username\": \"admin\",\n\t\"password\": \"myAdminPassword\"\n&rbrace;");
},
headersPlaceholder() {
return this.decodeHtml("&lbrace;\n\t\"Authorization\": \"Bearer abc123\",\n\t\"Content-Type\": \"application/json\"\n&rbrace;");
}
},
@ -296,7 +350,7 @@ export default {
},
"monitor.interval"(value, oldValue) {
// Link interval and retryInerval if they are the same value.
// Link interval and retryInterval if they are the same value.
if (this.monitor.retryInterval === oldValue) {
this.monitor.retryInterval = value;
}
@ -350,6 +404,7 @@ export default {
type: "http",
name: "",
url: "https://",
method: "GET",
interval: 60,
retryInterval: this.interval,
maxretries: 0,
@ -384,9 +439,43 @@ export default {
},
isInputValid() {
if (this.monitor.body) {
try {
JSON.parse(this.monitor.body);
} catch (err) {
toast.error(this.$t("BodyInvalidFormat") + err.message);
return false;
}
}
if (this.monitor.headers) {
try {
JSON.parse(this.monitor.headers);
} catch (err) {
toast.error(this.$t("HeadersInvalidFormat") + err.message);
return false;
}
}
return true;
},
async submit() {
this.processing = true;
if (!this.isInputValid()) {
this.processing = false;
return;
}
// Beautiful the JSON format
if (this.monitor.body) {
this.monitor.body = JSON.stringify(JSON.parse(this.monitor.body), null, 4);
}
if (this.monitor.headers) {
this.monitor.headers = JSON.stringify(JSON.parse(this.monitor.headers), null, 4);
}
if (this.isAdd) {
this.$root.add(this.monitor, async (res) => {
@ -419,12 +508,22 @@ export default {
addedNotification(id) {
this.monitor.notificationIDList[id] = true;
},
decodeHtml(html) {
const txt = document.createElement("textarea");
txt.innerHTML = html;
return txt.value;
}
},
};
</script>
<style scoped>
<style lang="scss" scoped>
.shadow-box {
padding: 20px;
}
textarea {
min-height: 200px;
}
</style>

View file

@ -334,9 +334,9 @@
</template>
<template v-else-if="$i18n.locale === 'id-ID' ">
<p> Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>? </p>
<p> Ini untuk <strong>mereka yang memiliki otentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare. </p>
<p> Gunakan dengan hati-hati. </p>
<p>Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>?</p>
<p>Ini untuk <strong>mereka yang memiliki autentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare.</p>
<p>Gunakan dengan hati-hati.</p>
</template>
<template v-else-if="$i18n.locale === 'ru-RU' ">

View file

@ -1,11 +1,11 @@
const { genSecret } = require("../src/util");
beforeAll(() => {
});
const { genSecret, sleep } = require("../src/util");
describe("Test genSecret", () => {
beforeAll(() => {
});
it("should be correct length", () => {
let secret = genSecret(-1);
expect(secret).toEqual("");
@ -34,4 +34,11 @@ describe("Test genSecret", () => {
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);
});