This commit is contained in:
Ioma Taani 2021-11-22 23:39:10 +01:00
commit bd3d34400d
7 changed files with 183 additions and 83 deletions

60
extra/remove-2fa.js Normal file
View file

@ -0,0 +1,60 @@
console.log("== Uptime Kuma Remove 2FA Tool ==");
console.log("Loading the database");
const Database = require("../server/database");
const { R } = require("redbean-node");
const readline = require("readline");
const TwoFA = require("../server/2fa");
const args = require("args-parser")(process.argv);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const main = async () => {
Database.init(args);
await Database.connect();
try {
// 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("Found user: " + user.username);
let ans = await question("Are you sure want to remove 2FA? [y/N]");
if (ans.toLowerCase() === "y") {
await TwoFA.disable2FA(user.id);
console.log("2FA has been removed successfully.");
}
}
} catch (e) {
console.error("Error: " + e.message);
}
await Database.close();
rl.close();
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,
};

View file

@ -41,6 +41,7 @@
"update-version": "node extra/update-version.js", "update-version": "node extra/update-version.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",
"remove-2fa": "node extra/remove-2fa.js",
"compile-install-script": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command ./extra/compile-install-script.ps1", "compile-install-script": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command ./extra/compile-install-script.ps1",
"test-install-script-centos7": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/centos7.dockerfile .", "test-install-script-centos7": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/centos7.dockerfile .",
"test-install-script-alpine3": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/alpine3.dockerfile .", "test-install-script-alpine3": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/alpine3.dockerfile .",

14
server/2fa.js Normal file
View file

@ -0,0 +1,14 @@
const { checkLogin } = require("./util-server");
const { R } = require("redbean-node");
class TwoFA {
static async disable2FA(userID) {
return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
userID,
]);
}
}
module.exports = TwoFA;

View file

@ -120,6 +120,7 @@ module.exports.io = io;
const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo } = require("./client"); const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo } = require("./client");
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler"); const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
const databaseSocketHandler = require("./socket-handlers/database-socket-handler"); const databaseSocketHandler = require("./socket-handlers/database-socket-handler");
const TwoFA = require("./2fa");
app.use(express.json()); app.use(express.json());
@ -420,10 +421,7 @@ exports.entryPage = "dashboard";
socket.on("disable2FA", async (callback) => { socket.on("disable2FA", async (callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
await TwoFA.disable2FA(socket.userID);
await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
socket.userID,
]);
callback({ callback({
ok: true, ok: true,

View file

@ -175,7 +175,7 @@ export default {
"Entry Page": "Pagina Principale", "Entry Page": "Pagina Principale",
statusPageNothing: "Non c'è nulla qui, aggiungere un gruppo oppure un monitoraggio.", statusPageNothing: "Non c'è nulla qui, aggiungere un gruppo oppure un monitoraggio.",
"No Services": "Nessun Servizio", "No Services": "Nessun Servizio",
"All Systems Operational": "Tutti i sistemi sono operativi", "All Systems Operational": "Tutti i sistemi sono funzionali",
"Partially Degraded Service": "Servizio parzialmente degradato", "Partially Degraded Service": "Servizio parzialmente degradato",
"Degraded Service": "Servizio degradato", "Degraded Service": "Servizio degradato",
"Add Group": "Aggiungi Gruppo", "Add Group": "Aggiungi Gruppo",
@ -304,7 +304,7 @@ export default {
PasswordsDoNotMatch: "Le password non corrispondono.", PasswordsDoNotMatch: "Le password non corrispondono.",
records: "records", records: "records",
"One record": "One record", "One record": "One record",
steamApiKeyDescription: "Per monitorare un server di gioco Steam si necessita della chiave Steam Web-API. È possibile registrare la propria chiave API qui: ", steamApiKeyDescription: "Per monitorare un server di gioco Steam si necessita della chiave Web-API di Steam. È possibile registrare la propria chiave API qui: ",
"Current User": "Utente corrente", "Current User": "Utente corrente",
recent: "Recenti", recent: "Recenti",
}; };

View file

@ -17,7 +17,7 @@ export default {
pauseMonitorMsg: "一時停止しますか?", pauseMonitorMsg: "一時停止しますか?",
Settings: "設定", Settings: "設定",
Dashboard: "ダッシュボード", Dashboard: "ダッシュボード",
"New Update": "New Update", "New Update": "新しいアップデート",
Language: "言語", Language: "言語",
Appearance: "外観", Appearance: "外観",
Theme: "テーマ", Theme: "テーマ",
@ -53,7 +53,7 @@ export default {
Ping: "Ping", Ping: "Ping",
"Monitor Type": "監視タイプ", "Monitor Type": "監視タイプ",
Keyword: "キーワード", Keyword: "キーワード",
"Friendly Name": "Friendly Name", "Friendly Name": "分かりやすい名前",
URL: "URL", URL: "URL",
Hostname: "ホスト名", Hostname: "ホスト名",
Port: "ポート", Port: "ポート",
@ -104,60 +104,60 @@ export default {
"Resolver Server": "問い合わせ先DNSサーバ", "Resolver Server": "問い合わせ先DNSサーバ",
"Resource Record Type": "DNSレコード設定", "Resource Record Type": "DNSレコード設定",
"Last Result": "最終結果", "Last Result": "最終結果",
"Create your admin account": "Create your admin account", "Create your admin account": "Adminアカウントの作成",
"Repeat Password": "Repeat Password", "Repeat Password": "パスワード確認",
respTime: "Resp. Time (ms)", respTime: "応答時間 (ms)",
notAvailableShort: "N/A", notAvailableShort: "N/A",
Create: "Create", Create: "作成",
clearEventsMsg: "Are you sure want to delete all events for this monitor?", clearEventsMsg: "この監視のすべての記録を削除してもよろしいですか?",
clearHeartbeatsMsg: "Are you sure want to delete all heartbeats for this monitor?", clearHeartbeatsMsg: "この監視のすべての異常記録を削除してもよろしいですか?",
confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?", confirmClearStatisticsMsg: "すべての統計を削除してもよろしいですか?",
"Clear Data": "Clear Data", "Clear Data": "データを削除",
Events: "Events", Events: "統計",
Heartbeats: "Heartbeats", Heartbeats: "異常記録",
"Auto Get": "Auto Get", "Auto Get": "自動取得",
enableDefaultNotificationDescription: "For every new monitor this notification will be enabled by default. You can still disable the notification separately for each monitor.", enableDefaultNotificationDescription: "監視を作成するごとに、この通知方法はデフォルトで有効になります。監視ごとに通知を無効にすることもできます。",
"Default enabled": "Default enabled", "Default enabled": "デフォルトで有効にする",
"Also apply to existing monitors": "Also apply to existing monitors", "Also apply to existing monitors": "既存のモニターにも適用する",
Export: "Export", Export: "エクスポート",
Import: "Import", Import: "インポート",
backupDescription: "You can backup all monitors and all notifications into a JSON file.", backupDescription: "すべての監視と通知方法をJSONファイルにできます。",
backupDescription2: "PS: History and event data is not included.", backupDescription2: "※ 履歴と統計のデータはバックアップされません。",
backupDescription3: "Sensitive data such as notification tokens is included in the export file, please keep it carefully.", backupDescription3: "通知に使用するトークンなどの機密データも含まれています。注意して扱ってください。",
alertNoFile: "Please select a file to import.", alertNoFile: "インポートするファイルを選択してください。",
alertWrongFileType: "Please select a JSON file.", alertWrongFileType: "JSONファイルを選択してください。",
twoFAVerifyLabel: "Please type in your token to verify that 2FA is working", twoFAVerifyLabel: "トークンを入力して、2段階認証を有効にします。",
tokenValidSettingsMsg: "Token is valid! You can now save the 2FA settings.", tokenValidSettingsMsg: "トークンの確認が完了しました! 「保存」をしてください。",
confirmEnableTwoFAMsg: "Are you sure you want to enable 2FA?", confirmEnableTwoFAMsg: "2段階認証を「有効」にします。よろしいですか?",
confirmDisableTwoFAMsg: "Are you sure you want to disable 2FA?", confirmDisableTwoFAMsg: "2段階認証を「無効」にします。よろしいですか?",
"Apply on all existing monitors": "Apply on all existing monitors", "Apply on all existing monitors": "既存のすべてのモニターに適用する",
"Verify Token": "Verify Token", "Verify Token": "認証する",
"Setup 2FA": "Setup 2FA", "Setup 2FA": "2段階認証の設定",
"Enable 2FA": "Enable 2FA", "Enable 2FA": "2段階認証を有効にする",
"Disable 2FA": "Disable 2FA", "Disable 2FA": "2段階認証を無効にする",
"2FA Settings": "2FA Settings", "2FA Settings": "2段階認証の設定",
"Two Factor Authentication": "Two Factor Authentication", "Two Factor Authentication": "2段階認証",
Active: "Active", Active: "Active",
Inactive: "Inactive", Inactive: "Inactive",
Token: "Token", Token: "Token",
"Show URI": "Show URI", "Show URI": "Show URI",
"Clear all statistics": "Clear all Statistics", "Clear all statistics": "すべての記録を削除",
retryCheckEverySecond: "Retry every {0} seconds.", retryCheckEverySecond: "Retry every {0} seconds.",
importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.", importHandleDescription: "同じ名前のすべての監視または通知方法を上書きしない場合は、「既存のをスキップ」を選択します。 「上書きする」は、既存のすべてのモニターと通知を削除します。",
confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.", confirmImportMsg: "バックアップをインポートしてもよろしいですか?希望するオプションを選択してください。",
"Heartbeat Retry Interval": "Heartbeat Retry Interval", "Heartbeat Retry Interval": "異常検知後の再試行間隔",
"Import Backup": "Import Backup", "Import Backup": "バックアップのインポート",
"Export Backup": "Export Backup", "Export Backup": "バックアップのエクスポート",
"Skip existing": "Skip existing", "Skip existing": "既存のをスキップする",
Overwrite: "Overwrite", Overwrite: "上書きする",
Options: "Options", Options: "オプション",
"Keep both": "Keep both", "Keep both": "どちらも保持する",
Tags: "Tags", Tags: "タグ",
"Add New below or Select...": "Add New below or Select...", "Add New below or Select...": "新規追加または選択...",
"Tag with this name already exist.": "Tag with this name already exist.", "Tag with this name already exist.": "この名前のタグはすでに存在しています。",
"Tag with this value already exist.": "Tag with this value already exist.", "Tag with this value already exist.": "この値のタグはすでに存在しています。",
color: "color", color: "",
"value (optional)": "value (optional)", "value (optional)": " (optional)",
Gray: "Gray", Gray: "Gray",
Red: "Red", Red: "Red",
Orange: "Orange", Orange: "Orange",
@ -166,20 +166,20 @@ export default {
Indigo: "Indigo", Indigo: "Indigo",
Purple: "Purple", Purple: "Purple",
Pink: "Pink", Pink: "Pink",
"Search...": "Search...", "Search...": "検索...",
"Avg. Ping": "Avg. Ping", "Avg. Ping": "平均Ping時間",
"Avg. Response": "Avg. Response", "Avg. Response": "平均応答時間",
"Entry Page": "Entry Page", "Entry Page": "エントリーページ",
statusPageNothing: "Nothing here, please add a group or a monitor.", statusPageNothing: "ここには何もありません。グループまたは監視を追加してください。",
"No Services": "No Services", "No Services": "No Services",
"All Systems Operational": "All Systems Operational", "All Systems Operational": "すべてのサービスが稼働中",
"Partially Degraded Service": "Partially Degraded Service", "Partially Degraded Service": "部分的にサービスが停止中",
"Degraded Service": "Degraded Service", "Degraded Service": "サービスが停止中",
"Add Group": "Add Group", "Add Group": "グループの追加",
"Add a monitor": "Add a monitor", "Add a monitor": "監視の追加",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "ステータスページ編集",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "ダッシュボード",
"Status Page": "Status Page", "Status Page": "ステータスページ",
telegram: "Telegram", telegram: "Telegram",
webhook: "Webhook", webhook: "Webhook",
smtp: "Email (SMTP)", smtp: "Email (SMTP)",

View file

@ -33,21 +33,22 @@ export default {
Appearance: "Giao diện", Appearance: "Giao diện",
Theme: "Theme", Theme: "Theme",
General: "Chung", General: "Chung",
"Primary Base URL": "URL chính",
Version: "Phiên bản", Version: "Phiên bản",
"Check Update On GitHub": "Kiểm tra bản cập nhật mới trên GitHub", "Check Update On GitHub": "Kiểm tra bản cập nhật mới trên GitHub",
List: "List", List: "List",
Add: "Thêm", Add: "Thêm",
"Add New Monitor": "Thêm mới Monitor", "Add New Monitor": "Thêm mới Monitor",
"Quick Stats": "Thống kê nhanh", "Quick Stats": "Thống kê nhanh",
Up: "Lên", Up: "Up",
Down: "Xuống", Down: "Down",
Pending: "Chờ xử lý", Pending: "Chờ xử lý",
Unknown: "Không xác định", Unknown: "Không xác định",
Pause: "Tạm dừng", Pause: "Tạm dừng",
Name: "Tên", Name: "Tên",
Status: "Trạng thái", Status: "Trạng thái",
DateTime: "Ngày tháng", DateTime: "Ngày tháng",
Message: "Tin nhắn", Message: "Trạng thái request",
"No important events": "Không có sự kiện quan trọng nào", "No important events": "Không có sự kiện quan trọng nào",
Resume: "Khôi phục", Resume: "Khôi phục",
Edit: "Sửa", Edit: "Sửa",
@ -64,17 +65,20 @@ export default {
Ping: "Ping", Ping: "Ping",
"Monitor Type": "Kiểu monitor", "Monitor Type": "Kiểu monitor",
Keyword: "Từ khoá", Keyword: "Từ khoá",
"Friendly Name": "Tên dễ hiểu", "Friendly Name": "Tên monitor",
URL: "URL", URL: "URL",
Hostname: "Hostname", Hostname: "Hostname",
Port: "Port", Port: "Port",
"Heartbeat Interval": "Tần suất heartbeat", "Heartbeat Interval": "Tần suất kiểm tra",
Retries: "Thử lại", Retries: "Thử lại",
"Heartbeat Retry Interval": "Tần suất thử lại của Heartbeat", "Heartbeat Retry Interval": "Tần suất kiểm tra lại",
Advanced: "Nâng cao", Advanced: "Nâng cao",
"Upside Down Mode": "Trạng thái đảo ngược", "Upside Down Mode": "Chế độ đảo ngược",
"Max. Redirects": "Chuyển hướng tối đa", "Max. Redirects": "Số chuyển hướng tối đa",
"Accepted Status Codes": "Codes trạng thái chấp nhận", "Accepted Status Codes": "Codes trạng thái chấp nhận",
"Push URL": "Push URL",
needPushEvery: "Bạn nên gọi URL mỗi {0} giây.",
pushOptionalParams: "Tuỳ chỉnh parameters: {0}",
Save: "Lưu", Save: "Lưu",
Notifications: "Thông báo", Notifications: "Thông báo",
"Not available, please setup.": "Chưa sẵn sàng, hãy cài đặt.", "Not available, please setup.": "Chưa sẵn sàng, hãy cài đặt.",
@ -131,7 +135,7 @@ export default {
Events: "Sự kiện", Events: "Sự kiện",
Heartbeats: "Heartbeats", Heartbeats: "Heartbeats",
"Auto Get": "Tự động lấy", "Auto Get": "Tự động lấy",
backupDescription: "Bạn có thể sao lưu tất cả các màn hình và tất cả các thông báo vào một file JSON.", backupDescription: "Bạn có thể sao lưu tất cả các monitor và tất cả các thông báo vào một file JSON.",
backupDescription2: "PS: Không bao gồm dữ liệu lịch sử các sự kiện.", backupDescription2: "PS: Không bao gồm dữ liệu lịch sử các sự kiện.",
backupDescription3: "Hãy lưu giữ file này cẩn thận vì trong file đó chứa cả các mã token thông báo.", backupDescription3: "Hãy lưu giữ file này cẩn thận vì trong file đó chứa cả các mã token thông báo.",
alertNoFile: "Hãy chọn file để khôi phục.", alertNoFile: "Hãy chọn file để khôi phục.",
@ -171,7 +175,7 @@ export default {
"Entry Page": "Entry Page", "Entry Page": "Entry Page",
statusPageNothing: "Không có gì, hãy thêm nhóm monitor hoặc monitor.", statusPageNothing: "Không có gì, hãy thêm nhóm monitor hoặc monitor.",
"No Services": "Không có dịch vụ", "No Services": "Không có dịch vụ",
"All Systems Operational": "Tất cả các hệ thống hoạt động", "All Systems Operational": "Tất cả các hệ thống hoạt động bình thường",
"Partially Degraded Service": "Dịch vụ xuống cấp một phần", "Partially Degraded Service": "Dịch vụ xuống cấp một phần",
"Degraded Service": "Degraded Service", "Degraded Service": "Degraded Service",
"Add Group": "Thêm nhóm", "Add Group": "Thêm nhóm",
@ -184,7 +188,7 @@ export default {
Required: "Bắt buộc", Required: "Bắt buộc",
telegram: "Telegram", telegram: "Telegram",
"Bot Token": "Bot Token", "Bot Token": "Bot Token",
"You can get a token from": "Bạn có thể lấy mã token từ", wayToGetTelegramToken: "Bạn có thể lấy mã token từ",
"Chat ID": "Chat ID", "Chat ID": "Chat ID",
supportTelegramChatID: "Hỗ trợ chat trực tiếp / Nhóm / Kênh Chat ID", supportTelegramChatID: "Hỗ trợ chat trực tiếp / Nhóm / Kênh Chat ID",
wayToGetTelegramChatID: "Bạn có thể lấy chat id của mình bằng cách gửi tin nhắn tới bot và truy cập url này để xem chat_id:", wayToGetTelegramChatID: "Bạn có thể lấy chat id của mình bằng cách gửi tin nhắn tới bot và truy cập url này để xem chat_id:",
@ -200,6 +204,7 @@ export default {
secureOptionTLS: "TLS (465)", secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Bỏ qua lỗi TLS", "Ignore TLS Error": "Bỏ qua lỗi TLS",
"From Email": "Từ Email", "From Email": "Từ Email",
emailCustomSubject: "Tuỳ chỉnh tiêu đề",
"To Email": "Tới Email", "To Email": "Tới Email",
smtpCC: "CC", smtpCC: "CC",
smtpBCC: "BCC", smtpBCC: "BCC",
@ -212,7 +217,7 @@ export default {
teams: "Microsoft Teams", teams: "Microsoft Teams",
"Webhook URL": "Webhook URL", "Webhook URL": "Webhook URL",
wayToGetTeamsURL: "Bạn có thể học cách tạo webhook url {0}.", wayToGetTeamsURL: "Bạn có thể học cách tạo webhook url {0}.",
signal: "Signal", signal: "Tín hiệu",
Number: "Số", Number: "Số",
Recipients: "Người nhận", Recipients: "Người nhận",
needSignalAPI: "Bạn cần một tín hiệu client với REST API.", needSignalAPI: "Bạn cần một tín hiệu client với REST API.",
@ -235,8 +240,9 @@ export default {
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS", promosms: "PromoSMS",
clicksendsms: "ClickSend SMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Thông báo (Hỗ trợ 50+ dịch vụ thông báo)", apprise: "Apprise (Hỗ trợ 50+ dịch vụ thông báo)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",
line: "Line Messenger", line: "Line Messenger",
mattermost: "Mattermost", mattermost: "Mattermost",
@ -250,8 +256,11 @@ export default {
"SMS Type": "SMS Type", "SMS Type": "SMS Type",
octopushTypePremium: "Premium (Nhanh - Khuyến nghị nên dùng cho cảnh báo)", octopushTypePremium: "Premium (Nhanh - Khuyến nghị nên dùng cho cảnh báo)",
octopushTypeLowCost: "Giá rẻ (Chậm, thỉnh thoảng bị chặn)", octopushTypeLowCost: "Giá rẻ (Chậm, thỉnh thoảng bị chặn)",
checkPrice: "Kiểm tra giá {0}:",
apiCredentials: "API credentials",
octopushLegacyHint: "Bạn muốn sử dụng phiên bản cũ của Octopush (2011-2020) hay phiên bản mới?",
"Check octopush prices": "Kiểm tra giá octopush {0}.", "Check octopush prices": "Kiểm tra giá octopush {0}.",
octopushPhoneNumber: "Số điện thoại (Định dạng intl, vd : +33612345678) ", octopushPhoneNumber: "Số điện thoại (Định dạng intl, vd : +84123456789) ",
octopushSMSSender: "SMS người gửi : 3-11 ký tự chữ, số và dấu cách (a-zA-Z0-9)", octopushSMSSender: "SMS người gửi : 3-11 ký tự chữ, số và dấu cách (a-zA-Z0-9)",
"LunaSea Device ID": "LunaSea ID thiết bị", "LunaSea Device ID": "LunaSea ID thiết bị",
"Apprise URL": "Apprise URL", "Apprise URL": "Apprise URL",
@ -280,4 +289,22 @@ export default {
promosmsPhoneNumber: "Số điện thoại (Bỏ qua mã vùng với người Ba Lan)", promosmsPhoneNumber: "Số điện thoại (Bỏ qua mã vùng với người Ba Lan)",
promosmsSMSSender: "SMS Tên người gửi: Tên đã đăng ký trước hoặc tên mặc định: InfoSMS, SMS Info, MaxSMS, INFO, SMS", promosmsSMSSender: "SMS Tên người gửi: Tên đã đăng ký trước hoặc tên mặc định: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
"Feishu WebHookUrl": "Feishu WebHookUrl", "Feishu WebHookUrl": "Feishu WebHookUrl",
matrixHomeserverURL: "Homeserver URL (với http(s):// và port tuỳ chỉnh)",
"Internal Room Id": "Room ID Nội bộ",
matrixDesc1: "Bạn có thể tìm thấy room ID nội bộ bằng cách tìm trong mục advanced của phần room settings trong Matrix client của bạn. Nó có dạng giống như !QMdRCpUIfLwsfjxye6:home.server.",
matrixDesc2: "Bạn nên tạo người dùng mới và đừng sử dụng mã token truy cập của Matrix user vì nó sẽ cho phép truy cập toàn quyền vào tài khoản của bạn và tất cả các phòng bạn đã tham gia. Thay vào đó, hãy tạo một người dùng mới và chỉ mời người đó vào phòng mà bạn muốn nhận thông báo. Bạn có thể lấy được mã token truy cập bằng cách chạy {0}",
Method: "Method",
Body: "Body",
Headers: "Headers",
PushUrl: "Push URL",
HeadersInvalidFormat: "Header request không hợp lệ JSON: ",
BodyInvalidFormat: "Tequest body không hợp lệ JSON: ",
"Monitor History": "Lịch sử Monitor",
clearDataOlderThan: "Giữ dữ liệu lịch sử monitor {0} ngày.",
PasswordsDoNotMatch: "Passwords không khớp.",
records: "records",
"One record": "One record",
steamApiKeyDescription: "Để monitor các Steam Game Server bạn cần một Steam Web-API key. Bạn có thể đăng ký API key tại đây: ",
"Current User": "User hiện tại",
recent: "Gần đây",
}; };