mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-12-24 22:14:47 +02:00
Merge from upstream
This commit is contained in:
commit
912686a299
@ -32,6 +32,10 @@ if (! exists) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit updated files
|
||||
* @param {string} version Version to update to
|
||||
*/
|
||||
function commit(version) {
|
||||
let msg = "Update to " + version;
|
||||
|
||||
@ -47,6 +51,10 @@ function commit(version) {
|
||||
console.log(res.stdout.toString().trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tag with the specified version
|
||||
* @param {string} version Tag to create
|
||||
*/
|
||||
function tag(version) {
|
||||
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
||||
console.log(res.stdout.toString().trim());
|
||||
@ -55,6 +63,11 @@ function tag(version) {
|
||||
console.log(res.stdout.toString().trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tag exists for the specified version
|
||||
* @param {string} version Version to check
|
||||
* @returns {boolean} Does the tag already exist
|
||||
*/
|
||||
function tagExists(version) {
|
||||
if (! version) {
|
||||
throw new Error("invalid version");
|
||||
|
@ -25,6 +25,10 @@ if (platform === "linux/amd64") {
|
||||
const file = fs.createWriteStream("cloudflared.deb");
|
||||
get("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-" + arch + ".deb");
|
||||
|
||||
/**
|
||||
* Download specified file
|
||||
* @param {string} url URL to request
|
||||
*/
|
||||
function get(url) {
|
||||
http.get(url, function (res) {
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
|
@ -43,6 +43,11 @@ const main = async () => {
|
||||
console.log("Finished.");
|
||||
};
|
||||
|
||||
/**
|
||||
* Ask question of user
|
||||
* @param {string} question Question to ask
|
||||
* @returns {Promise<string>} Users response
|
||||
*/
|
||||
function question(question) {
|
||||
return new Promise((resolve) => {
|
||||
rl.question(question, (answer) => {
|
||||
|
@ -53,6 +53,11 @@ const main = async () => {
|
||||
console.log("Finished.");
|
||||
};
|
||||
|
||||
/**
|
||||
* Ask question of user
|
||||
* @param {string} question Question to ask
|
||||
* @returns {Promise<string>} Users response
|
||||
*/
|
||||
function question(question) {
|
||||
return new Promise((resolve) => {
|
||||
rl.question(question, (answer) => {
|
||||
|
@ -135,6 +135,11 @@ server.listen({
|
||||
udp: 5300
|
||||
});
|
||||
|
||||
/**
|
||||
* Get human readable request type from request code
|
||||
* @param {number} code Request code to translate
|
||||
* @returns {string} Human readable request type
|
||||
*/
|
||||
function type(code) {
|
||||
for (let name in Packet.TYPE) {
|
||||
if (Packet.TYPE[name] === code) {
|
||||
|
@ -11,6 +11,7 @@ class SimpleMqttServer {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/** Start the MQTT server */
|
||||
start() {
|
||||
this.server.listen(this.port, () => {
|
||||
console.log("server started and listening on port ", this.port);
|
||||
|
@ -36,10 +36,8 @@ if (! exists) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the version number in package.json and commits it to git.
|
||||
* @param {string} version - The new version number
|
||||
*
|
||||
* Generated by Trelent
|
||||
* Commit updated files
|
||||
* @param {string} version Version to update to
|
||||
*/
|
||||
function commit(version) {
|
||||
let msg = "Update to " + version;
|
||||
@ -53,16 +51,19 @@ function commit(version) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tag with the specified version
|
||||
* @param {string} version Tag to create
|
||||
*/
|
||||
function tag(version) {
|
||||
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
||||
console.log(res.stdout.toString().trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given version is already tagged in the git repository.
|
||||
* @param {string} version - The version to check for.
|
||||
*
|
||||
* Generated by Trelent
|
||||
* Check if a tag exists for the specified version
|
||||
* @param {string} version Version to check
|
||||
* @returns {boolean} Does the tag already exist
|
||||
*/
|
||||
function tagExists(version) {
|
||||
if (! version) {
|
||||
|
@ -10,6 +10,10 @@ if (!newVersion) {
|
||||
|
||||
updateWiki(newVersion);
|
||||
|
||||
/**
|
||||
* Update the wiki with new version number
|
||||
* @param {string} newVersion Version to update to
|
||||
*/
|
||||
function updateWiki(newVersion) {
|
||||
const wikiDir = "./tmp/wiki";
|
||||
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
|
||||
@ -39,6 +43,10 @@ function updateWiki(newVersion) {
|
||||
safeDelete(wikiDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a directory exists and then delete it
|
||||
* @param {string} dir Directory to delete
|
||||
*/
|
||||
function safeDelete(dir) {
|
||||
if (fs.existsSync(dir)) {
|
||||
fs.rm(dir, {
|
||||
|
1865
package-lock.json
generated
1865
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"version": "1.19.4",
|
||||
"version": "1.19.6",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -39,7 +39,7 @@
|
||||
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
|
||||
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push",
|
||||
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
||||
"setup": "git checkout 1.19.4 && npm ci --production && npm run download-dist",
|
||||
"setup": "git checkout 1.19.6 && npm ci --production && npm run download-dist",
|
||||
"download-dist": "node extra/download-dist.js",
|
||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||
"reset-password": "node extra/reset-password.js",
|
||||
@ -67,7 +67,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "~1.7.3",
|
||||
"@louislam/ping": "~0.4.2-mod.0",
|
||||
"@louislam/ping": "~0.4.2-mod.1",
|
||||
"@louislam/sqlite3": "15.1.2",
|
||||
"args-parser": "~1.3.0",
|
||||
"axios": "~0.27.0",
|
||||
@ -110,7 +110,7 @@
|
||||
"prom-client": "~13.2.0",
|
||||
"prometheus-api-metrics": "~3.2.1",
|
||||
"protobufjs": "~7.1.1",
|
||||
"redbean-node": "0.1.4",
|
||||
"redbean-node": "~0.2.0",
|
||||
"redis": "~4.5.1",
|
||||
"socket.io": "~4.5.3",
|
||||
"socket.io-client": "~4.5.3",
|
||||
|
@ -63,6 +63,12 @@ function myAuthorizer(username, password, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use basic auth if auth is not disabled
|
||||
* @param {express.Request} req Express request object
|
||||
* @param {express.Response} res Express response object
|
||||
* @param {express.NextFunction} next
|
||||
*/
|
||||
exports.basicAuth = async function (req, res, next) {
|
||||
const middleware = basicAuth({
|
||||
authorizer: myAuthorizer,
|
||||
|
@ -37,6 +37,10 @@ class CacheableDnsHttpAgent {
|
||||
this.enable = isEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach cacheable to HTTP agent
|
||||
* @param {http.Agent} agent Agent to install
|
||||
*/
|
||||
static install(agent) {
|
||||
this.cacheable.install(agent);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ const initBackgroundJobs = function (args) {
|
||||
return bree;
|
||||
};
|
||||
|
||||
/** Stop all background jobs if running */
|
||||
const stopBackgroundJobs = function () {
|
||||
if (bree) {
|
||||
bree.stop();
|
||||
|
@ -112,6 +112,11 @@ class Maintenance extends BeanModel {
|
||||
return this.toPublicJSON(timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of weekdays that the maintenance is active for
|
||||
* Monday=1, Tuesday=2 etc.
|
||||
* @returns {number[]} Array of active weekdays
|
||||
*/
|
||||
getDayOfWeekList() {
|
||||
log.debug("timeslot", "List: " + this.weekdays);
|
||||
return JSON.parse(this.weekdays).sort(function (a, b) {
|
||||
@ -119,12 +124,20 @@ class Maintenance extends BeanModel {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of days in month that maintenance is active for
|
||||
* @returns {number[]} Array of active days in month
|
||||
*/
|
||||
getDayOfMonthList() {
|
||||
return JSON.parse(this.days_of_month).sort(function (a, b) {
|
||||
return a - b;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start date and time for maintenance
|
||||
* @returns {dayjs.Dayjs} Start date and time
|
||||
*/
|
||||
getStartDateTime() {
|
||||
let startOfTheDay = dayjs.utc(this.start_date).format("HH:mm");
|
||||
log.debug("timeslot", "startOfTheDay: " + startOfTheDay);
|
||||
@ -137,6 +150,10 @@ class Maintenance extends BeanModel {
|
||||
return dayjs.utc(this.start_date).add(startTimeSecond, "second");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duraction of maintenance in seconds
|
||||
* @returns {number} Duration of maintenance
|
||||
*/
|
||||
getDuration() {
|
||||
let duration = dayjs.utc(this.end_time, "HH:mm").diff(dayjs.utc(this.start_time, "HH:mm"), "second");
|
||||
// Add 24hours if it is across day
|
||||
@ -146,6 +163,12 @@ class Maintenance extends BeanModel {
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert data from socket to bean
|
||||
* @param {Bean} bean Bean to fill in
|
||||
* @param {Object} obj Data to fill bean with
|
||||
* @returns {Bean} Filled bean
|
||||
*/
|
||||
static jsonToBean(bean, obj) {
|
||||
if (obj.id) {
|
||||
bean.id = obj.id;
|
||||
|
@ -6,6 +6,11 @@ const { UptimeKumaServer } = require("../uptime-kuma-server");
|
||||
|
||||
class MaintenanceTimeslot extends BeanModel {
|
||||
|
||||
/**
|
||||
* Return an object that ready to parse to JSON for public
|
||||
* Only show necessary data to public
|
||||
* @returns {Object}
|
||||
*/
|
||||
async toPublicJSON() {
|
||||
const serverTimezoneOffset = UptimeKumaServer.getInstance().getTimezoneOffset();
|
||||
|
||||
@ -21,6 +26,10 @@ class MaintenanceTimeslot extends BeanModel {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object that ready to parse to JSON
|
||||
* @returns {Object}
|
||||
*/
|
||||
async toJSON() {
|
||||
return await this.toPublicJSON();
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ class Monitor extends BeanModel {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
sendUrl: this.sendUrl,
|
||||
maintenance: await Monitor.isUnderMaintenance(this.id),
|
||||
};
|
||||
|
||||
if (this.sendUrl) {
|
||||
@ -496,13 +495,17 @@ class Monitor extends BeanModel {
|
||||
|
||||
const options = {
|
||||
url: `/containers/${this.docker_container}/json`,
|
||||
timeout: this.interval * 1000 * 0.8,
|
||||
headers: {
|
||||
"Accept": "*/*",
|
||||
"User-Agent": "Uptime-Kuma/" + version,
|
||||
},
|
||||
httpsAgent: new https.Agent({
|
||||
httpsAgent: CacheableDnsHttpAgent.getHttpsAgent({
|
||||
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
|
||||
rejectUnauthorized: ! this.getIgnoreTls(),
|
||||
rejectUnauthorized: !this.getIgnoreTls(),
|
||||
}),
|
||||
httpAgent: CacheableDnsHttpAgent.getHttpAgent({
|
||||
maxCachedSessions: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
@ -765,6 +768,13 @@ class Monitor extends BeanModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request using axios
|
||||
* @param {Object} options Options for Axios
|
||||
* @param {boolean} finalCall Should this be the final call i.e
|
||||
* don't retry on faliure
|
||||
* @returns {Object} Axios response
|
||||
*/
|
||||
async makeAxiosRequest(options, finalCall = false) {
|
||||
try {
|
||||
let res;
|
||||
@ -1246,6 +1256,7 @@ class Monitor extends BeanModel {
|
||||
return maintenance.count !== 0;
|
||||
}
|
||||
|
||||
/** Make sure monitor interval is between bounds */
|
||||
validate() {
|
||||
if (this.interval > MAX_INTERVAL_SECOND) {
|
||||
throw new Error(`Interval cannot be more than ${MAX_INTERVAL_SECOND} seconds`);
|
||||
|
@ -8,6 +8,14 @@ class PromoSMS extends NotificationProvider {
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
|
||||
if (notification.promosmsAllowLongSMS === undefined) {
|
||||
notification.promosmsAllowLongSMS = false;
|
||||
}
|
||||
|
||||
//TODO: Add option for enabling special characters. It will decrese message max length from 160 to 70 chars.
|
||||
//Lets remove non ascii char
|
||||
let cleanMsg = msg.replace(/[^\x00-\x7F]/g, "");
|
||||
|
||||
try {
|
||||
let config = {
|
||||
headers: {
|
||||
@ -18,8 +26,9 @@ class PromoSMS extends NotificationProvider {
|
||||
};
|
||||
let data = {
|
||||
"recipients": [ notification.promosmsPhoneNumber ],
|
||||
//Lets remove non ascii char
|
||||
"text": msg.replace(/[^\x00-\x7F]/g, ""),
|
||||
//Trim message to maximum length of 1 SMS or 4 if we allowed long messages
|
||||
"text": notification.promosmsAllowLongSMS ? cleanMsg.substring(0, 639) : cleanMsg.substring(0, 159),
|
||||
"long-sms": notification.promosmsAllowLongSMS,
|
||||
"type": Number(notification.promosmsSMSType),
|
||||
"sender": notification.promosmsSenderName
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ class Pushover extends NotificationProvider {
|
||||
let pushoverlink = "https://api.pushover.net/1/messages.json";
|
||||
|
||||
let data = {
|
||||
"message": "<b>Message</b>:" + msg,
|
||||
"message": msg,
|
||||
"user": notification.pushoveruserkey,
|
||||
"token": notification.pushoverapptoken,
|
||||
"sound": notification.pushoversounds,
|
||||
|
@ -21,6 +21,12 @@ class ServerChan extends NotificationProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted title for message
|
||||
* @param {?Object} monitorJSON Monitor details (For Up/Down only)
|
||||
* @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only)
|
||||
* @returns {string} Formatted title
|
||||
*/
|
||||
checkStatus(heartbeatJSON, monitorJSON) {
|
||||
let title = "UptimeKuma Message";
|
||||
if (heartbeatJSON != null && heartbeatJSON["status"] === UP) {
|
||||
|
113
server/notification-providers/splunk.js
Normal file
113
server/notification-providers/splunk.js
Normal file
@ -0,0 +1,113 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
|
||||
const { setting } = require("../util-server");
|
||||
let successMessage = "Sent Successfully.";
|
||||
|
||||
class Splunk extends NotificationProvider {
|
||||
name = "Splunk";
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
try {
|
||||
if (heartbeatJSON == null) {
|
||||
const title = "Uptime Kuma Alert";
|
||||
const monitor = {
|
||||
type: "ping",
|
||||
url: "Uptime Kuma Test Button",
|
||||
};
|
||||
return this.postNotification(notification, title, msg, monitor, "trigger");
|
||||
}
|
||||
|
||||
if (heartbeatJSON.status === UP) {
|
||||
const title = "Uptime Kuma Monitor ✅ Up";
|
||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "recovery");
|
||||
}
|
||||
|
||||
if (heartbeatJSON.status === DOWN) {
|
||||
const title = "Uptime Kuma Monitor 🔴 Down";
|
||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "trigger");
|
||||
}
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if result is successful, result code should be in range 2xx
|
||||
* @param {Object} result Axios response object
|
||||
* @throws {Error} The status code is not in range 2xx
|
||||
*/
|
||||
checkResult(result) {
|
||||
if (result.status == null) {
|
||||
throw new Error("Splunk notification failed with invalid response!");
|
||||
}
|
||||
if (result.status < 200 || result.status >= 300) {
|
||||
throw new Error("Splunk notification failed with status code " + result.status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message
|
||||
* @param {BeanModel} notification Message title
|
||||
* @param {string} title Message title
|
||||
* @param {string} body Message
|
||||
* @param {Object} monitorInfo Monitor details (For Up/Down only)
|
||||
* @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve)
|
||||
* @returns {string}
|
||||
*/
|
||||
async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") {
|
||||
|
||||
let monitorUrl;
|
||||
if (monitorInfo.type === "port") {
|
||||
monitorUrl = monitorInfo.hostname;
|
||||
if (monitorInfo.port) {
|
||||
monitorUrl += ":" + monitorInfo.port;
|
||||
}
|
||||
} else if (monitorInfo.hostname != null) {
|
||||
monitorUrl = monitorInfo.hostname;
|
||||
} else {
|
||||
monitorUrl = monitorInfo.url;
|
||||
}
|
||||
|
||||
if (eventAction === "recovery") {
|
||||
if (notification.splunkAutoResolve === "0") {
|
||||
return "No action required";
|
||||
}
|
||||
eventAction = notification.splunkAutoResolve;
|
||||
} else {
|
||||
eventAction = notification.splunkSeverity;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: notification.splunkRestURL,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
data: {
|
||||
message_type: eventAction,
|
||||
state_message: `[${title}] [${monitorUrl}] ${body}`,
|
||||
entity_display_name: "Uptime Kuma Alert: " + monitorInfo.name,
|
||||
routing_key: notification.pagerdutyIntegrationKey,
|
||||
entity_id: "Uptime Kuma/" + monitorInfo.id,
|
||||
}
|
||||
};
|
||||
|
||||
const baseURL = await setting("primaryBaseURL");
|
||||
if (baseURL && monitorInfo) {
|
||||
options.client = "Uptime Kuma";
|
||||
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);
|
||||
}
|
||||
|
||||
let result = await axios.request(options);
|
||||
this.checkResult(result);
|
||||
if (result.statusText != null) {
|
||||
return "Splunk notification succeed: " + result.statusText;
|
||||
}
|
||||
|
||||
return successMessage;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Splunk;
|
@ -40,6 +40,7 @@ const Stackfield = require("./notification-providers/stackfield");
|
||||
const Teams = require("./notification-providers/teams");
|
||||
const TechulusPush = require("./notification-providers/techulus-push");
|
||||
const Telegram = require("./notification-providers/telegram");
|
||||
const Splunk = require("./notification-providers/splunk");
|
||||
const Webhook = require("./notification-providers/webhook");
|
||||
const WeCom = require("./notification-providers/wecom");
|
||||
const GoAlert = require("./notification-providers/goalert");
|
||||
@ -100,6 +101,7 @@ class Notification {
|
||||
new Teams(),
|
||||
new TechulusPush(),
|
||||
new Telegram(),
|
||||
new Splunk(),
|
||||
new Webhook(),
|
||||
new WeCom(),
|
||||
new GoAlert(),
|
||||
|
@ -99,6 +99,7 @@ class Prometheus {
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove monitor from prometheus */
|
||||
remove() {
|
||||
try {
|
||||
monitorCertDaysRemaining.remove(this.monitorLabelValues);
|
||||
|
@ -6,10 +6,10 @@ class UptimeCacheList {
|
||||
static list = {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param monitorID
|
||||
* @param duration
|
||||
* @return number
|
||||
* Get the uptime for a specific period
|
||||
* @param {number} monitorID
|
||||
* @param {number} duration
|
||||
* @return {number}
|
||||
*/
|
||||
static getUptime(monitorID, duration) {
|
||||
if (UptimeCacheList.list[monitorID] && UptimeCacheList.list[monitorID][duration]) {
|
||||
@ -20,6 +20,12 @@ class UptimeCacheList {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add uptime for specified monitor
|
||||
* @param {number} monitorID
|
||||
* @param {number} duration
|
||||
* @param {number} uptime Uptime to add
|
||||
*/
|
||||
static addUptime(monitorID, duration, uptime) {
|
||||
log.debug("UptimeCacheList", "addUptime: " + monitorID + " " + duration);
|
||||
if (!UptimeCacheList.list[monitorID]) {
|
||||
@ -28,6 +34,10 @@ class UptimeCacheList {
|
||||
UptimeCacheList.list[monitorID][duration] = uptime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache for specified monitor
|
||||
* @param {number} monitorID
|
||||
*/
|
||||
static clearCache(monitorID) {
|
||||
log.debug("UptimeCacheList", "clearCache: " + monitorID);
|
||||
delete UptimeCacheList.list[monitorID];
|
||||
|
@ -86,6 +86,7 @@ class UptimeKumaServer {
|
||||
this.io = new Server(this.httpServer);
|
||||
}
|
||||
|
||||
/** Initialise app after the database has been set up */
|
||||
async initAfterDatabaseReady() {
|
||||
await CacheableDnsHttpAgent.update();
|
||||
|
||||
@ -98,6 +99,11 @@ class UptimeKumaServer {
|
||||
this.generateMaintenanceTimeslotsInterval = setInterval(this.generateMaintenanceTimeslots, 60 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send list of monitors to client
|
||||
* @param {Socket} socket
|
||||
* @returns {Object} List of monitors
|
||||
*/
|
||||
async sendMonitorList(socket) {
|
||||
let list = await this.getMonitorJSONList(socket.userID);
|
||||
this.io.to(socket.userID).emit("monitorList", list);
|
||||
@ -134,6 +140,11 @@ class UptimeKumaServer {
|
||||
return await this.sendMaintenanceListByUserID(socket.userID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send list of maintenances to user
|
||||
* @param {number} userID
|
||||
* @returns {Object}
|
||||
*/
|
||||
async sendMaintenanceListByUserID(userID) {
|
||||
let list = await this.getMaintenanceJSONList(userID);
|
||||
this.io.to(userID).emit("maintenanceList", list);
|
||||
@ -185,6 +196,11 @@ class UptimeKumaServer {
|
||||
errorLogStream.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IP of the client connected to the socket
|
||||
* @param {Socket} socket
|
||||
* @returns {string}
|
||||
*/
|
||||
async getClientIP(socket) {
|
||||
let clientIP = socket.client.conn.remoteAddress;
|
||||
|
||||
@ -203,6 +219,12 @@ class UptimeKumaServer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get the current server timezone
|
||||
* If this fails, fall back to environment variables and then make a
|
||||
* guess.
|
||||
* @returns {string}
|
||||
*/
|
||||
async getTimezone() {
|
||||
let timezone = await Settings.get("serverTimezone");
|
||||
if (timezone) {
|
||||
@ -214,16 +236,25 @@ class UptimeKumaServer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current offset
|
||||
* @returns {string}
|
||||
*/
|
||||
getTimezoneOffset() {
|
||||
return dayjs().format("Z");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current server timezone and environment variables
|
||||
* @param {string} timezone
|
||||
*/
|
||||
async setTimezone(timezone) {
|
||||
await Settings.set("serverTimezone", timezone, "general");
|
||||
process.env.TZ = timezone;
|
||||
dayjs.tz.setDefault(timezone);
|
||||
}
|
||||
|
||||
/** Load the timeslots for maintenance */
|
||||
async generateMaintenanceTimeslots() {
|
||||
|
||||
let list = await R.find("maintenance_timeslot", " generated_next = 0 AND start_date <= DATETIME('now') ");
|
||||
@ -237,6 +268,7 @@ class UptimeKumaServer {
|
||||
|
||||
}
|
||||
|
||||
/** Stop the server */
|
||||
async stop() {
|
||||
clearTimeout(this.generateMaintenanceTimeslotsInterval);
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ exports.pingAsync = function (hostname, ipv6 = false) {
|
||||
ping.promise.probe(hostname, {
|
||||
v6: ipv6,
|
||||
min_reply: 1,
|
||||
timeout: 10,
|
||||
deadline: 10,
|
||||
}).then((res) => {
|
||||
// If ping failed, it will set field to unknown
|
||||
if (res.alive) {
|
||||
@ -137,7 +137,7 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
||||
const { port, username, password, interval = 20 } = options;
|
||||
|
||||
// Adds MQTT protocol to the hostname if not already present
|
||||
if (!/^(?:http|mqtt)s?:\/\//.test(hostname)) {
|
||||
if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) {
|
||||
hostname = "mqtt://" + hostname;
|
||||
}
|
||||
|
||||
@ -147,10 +147,11 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
||||
reject(new Error("Timeout"));
|
||||
}, interval * 1000 * 0.8);
|
||||
|
||||
log.debug("mqtt", "MQTT connecting");
|
||||
const mqttUrl = `${hostname}:${port}`;
|
||||
|
||||
let client = mqtt.connect(hostname, {
|
||||
port,
|
||||
log.debug("mqtt", `MQTT connecting to ${mqttUrl}`);
|
||||
|
||||
let client = mqtt.connect(mqttUrl, {
|
||||
username,
|
||||
password
|
||||
});
|
||||
@ -282,18 +283,23 @@ exports.postgresQuery = function (connectionString, query) {
|
||||
|
||||
const client = new Client({ connectionString });
|
||||
|
||||
client.connect();
|
||||
|
||||
return client.query(query)
|
||||
.then(res => {
|
||||
resolve(res);
|
||||
})
|
||||
.catch(err => {
|
||||
client.connect((err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
})
|
||||
.finally(() => {
|
||||
client.end();
|
||||
});
|
||||
} else {
|
||||
// Connected here
|
||||
client.query(query, (err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(res);
|
||||
}
|
||||
client.end();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -91,11 +91,16 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** Confirm deletion of docker host */
|
||||
deleteConfirm() {
|
||||
this.modal.hide();
|
||||
this.$refs.confirmDelete.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show specified docker host
|
||||
* @param {number} dockerHostID
|
||||
*/
|
||||
show(dockerHostID) {
|
||||
if (dockerHostID) {
|
||||
let found = false;
|
||||
@ -126,6 +131,7 @@ export default {
|
||||
this.modal.show();
|
||||
},
|
||||
|
||||
/** Add docker host */
|
||||
submit() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("addDockerHost", this.dockerHost, this.id, (res) => {
|
||||
@ -144,6 +150,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Test the docker host */
|
||||
test() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("testDockerHost", this.dockerHost, (res) => {
|
||||
@ -152,6 +159,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Delete this docker host */
|
||||
deleteDockerHost() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("deleteDockerHost", this.id, (res) => {
|
||||
|
@ -16,7 +16,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="tag-color" class="form-label">{{ $t("Color") }}</label>
|
||||
<label for="tag-color" class="form-label">{{ $t("color") }}</label>
|
||||
<div class="d-flex">
|
||||
<div class="col-8 pe-1">
|
||||
<vue-multiselect
|
||||
|
@ -3,6 +3,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DOWN, MAINTENANCE, PENDING, UP } from "../util.ts";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/** Monitor this represents */
|
||||
@ -24,7 +26,6 @@ export default {
|
||||
|
||||
computed: {
|
||||
uptime() {
|
||||
|
||||
if (this.type === "maintenance") {
|
||||
return this.$t("statusMaintenance");
|
||||
}
|
||||
@ -39,19 +40,19 @@ export default {
|
||||
},
|
||||
|
||||
color() {
|
||||
if (this.type === "maintenance" || this.monitor.maintenance) {
|
||||
if (this.lastHeartBeat.status === MAINTENANCE) {
|
||||
return "maintenance";
|
||||
}
|
||||
|
||||
if (this.lastHeartBeat.status === 0) {
|
||||
if (this.lastHeartBeat.status === DOWN) {
|
||||
return "danger";
|
||||
}
|
||||
|
||||
if (this.lastHeartBeat.status === 1) {
|
||||
if (this.lastHeartBeat.status === UP) {
|
||||
return "primary";
|
||||
}
|
||||
|
||||
if (this.lastHeartBeat.status === 2) {
|
||||
if (this.lastHeartBeat.status === PENDING) {
|
||||
return "warning";
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,10 @@
|
||||
<label for="promosms-sender-name" class="form-label">{{ $t("promosmsSMSSender") }}</label>
|
||||
<input id="promosms-sender-name" v-model="$parent.notification.promosmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input id="promosms-allow-long" v-model="$parent.notification.promosmsAllowLongSMS" type="checkbox" class="form-check-input">
|
||||
<label for="promosms-allow-long" class="form-label">{{ $t("promosmsAllowLongSMS") }}</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
32
src/components/notifications/Splunk.vue
Normal file
32
src/components/notifications/Splunk.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="splunk-rest-url" class="form-label">{{ $t("Splunk Rest URL") }}</label>
|
||||
<HiddenInput id="splunk-rest-url" v-model="$parent.notification.splunkRestURL" :required="true" autocomplete="false"></HiddenInput>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="splunk-severity" class="form-label">{{ $t("Severity") }}</label>
|
||||
<select id="splunk-severity" v-model="$parent.notification.splunkSeverity" class="form-select">
|
||||
<option value="INFO">{{ $t("info") }}</option>
|
||||
<option value="WARNING">{{ $t("warning") }}</option>
|
||||
<option value="CRITICAL" selected="selected">{{ $t("critical") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="splunk-resolve" class="form-label">{{ $t("Auto resolve or acknowledged") }}</label>
|
||||
<select id="splunk-resolve" v-model="$parent.notification.splunkAutoResolve" class="form-select">
|
||||
<option value="0" selected="selected">{{ $t("do nothing") }}</option>
|
||||
<option value="ACKNOWLEDGEMENT">{{ $t("auto acknowledged") }}</option>
|
||||
<option value="RECOVERY">{{ $t("auto resolve") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
};
|
||||
</script>
|
@ -42,6 +42,11 @@ export default {
|
||||
HiddenInput,
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Get the URL for telegram updates
|
||||
* @param {string} [mode=masked] Should the token be masked?
|
||||
* @returns {string} formatted URL
|
||||
*/
|
||||
telegramGetUpdatesURL(mode = "masked") {
|
||||
let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`;
|
||||
|
||||
@ -55,6 +60,8 @@ export default {
|
||||
|
||||
return `https://api.telegram.org/bot${token}/getUpdates`;
|
||||
},
|
||||
|
||||
/** Get the telegram chat ID */
|
||||
async autoGetTelegramChatID() {
|
||||
try {
|
||||
let res = await axios.get(this.telegramGetUpdatesURL("withToken"));
|
||||
|
@ -44,6 +44,7 @@ import Webhook from "./Webhook.vue";
|
||||
import WeCom from "./WeCom.vue";
|
||||
import GoAlert from "./GoAlert.vue";
|
||||
import ZohoCliq from "./ZohoCliq.vue";
|
||||
import Splunk from "./Splunk.vue";
|
||||
|
||||
/**
|
||||
* Manage all notification form.
|
||||
@ -92,6 +93,7 @@ const NotificationFormList = {
|
||||
"stackfield": Stackfield,
|
||||
"teams": Teams,
|
||||
"telegram": Telegram,
|
||||
"Splunk": Splunk,
|
||||
"webhook": Webhook,
|
||||
"WeCom": WeCom,
|
||||
"GoAlert": GoAlert,
|
||||
|
@ -191,6 +191,7 @@ export default {
|
||||
location.reload();
|
||||
},
|
||||
|
||||
/** Show confirmation dialog for disable auth */
|
||||
confirmDisableAuth() {
|
||||
this.$refs.confirmDisableAuth.show();
|
||||
},
|
||||
|
@ -2,6 +2,7 @@ import { createI18n } from "vue-i18n/dist/vue-i18n.esm-browser.prod.js";
|
||||
import en from "./lang/en.json";
|
||||
|
||||
const languageList = {
|
||||
"ar-SY": "العربية",
|
||||
"cs-CZ": "Čeština",
|
||||
"zh-HK": "繁體中文 (香港)",
|
||||
"bg-BG": "Български",
|
||||
@ -48,7 +49,7 @@ for (let lang in languageList) {
|
||||
};
|
||||
}
|
||||
|
||||
const rtlLangs = [ "fa" ];
|
||||
const rtlLangs = [ "fa", "ar-SY" ];
|
||||
|
||||
export const currentLocale = () => localStorage.locale
|
||||
|| languageList[navigator.language] && navigator.language
|
||||
|
@ -44,6 +44,7 @@ import {
|
||||
faWrench,
|
||||
faHeartbeat,
|
||||
faFilter,
|
||||
faInfoCircle,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(
|
||||
@ -88,6 +89,7 @@ library.add(
|
||||
faWrench,
|
||||
faHeartbeat,
|
||||
faFilter,
|
||||
faInfoCircle,
|
||||
);
|
||||
|
||||
export { FontAwesomeIcon };
|
||||
|
684
src/languages/ar-SY.js
Normal file
684
src/languages/ar-SY.js
Normal file
@ -0,0 +1,684 @@
|
||||
export default {
|
||||
languageName: "العربية",
|
||||
checkEverySecond: "تحقق من كل {0} ثانية",
|
||||
retryCheckEverySecond: "أعد محاولة كل {0} ثانية",
|
||||
resendEveryXTimes: "إعادة تقديم كل {0} مرات",
|
||||
resendDisabled: "إعادة الالتزام بالتعطيل",
|
||||
retriesDescription: "الحد الأقصى لإعادة المحاولة قبل تمييز الخدمة على أنها لأسفل وإرسال إشعار",
|
||||
ignoreTLSError: "تجاهل خطأ TLS/SSL لمواقع HTTPS",
|
||||
upsideDownModeDescription: "اقلب الحالة رأسًا على عقب. إذا كانت الخدمة قابلة للوصول إلى أسفل.",
|
||||
maxRedirectDescription: "الحد الأقصى لعدد إعادة التوجيه لمتابعة. ضبط على 0 لتعطيل إعادة التوجيه.",
|
||||
enableGRPCTls: "السماح لإرسال طلب GRPC مع اتصال TLS",
|
||||
grpcMethodDescription: "يتم تحويل اسم الطريقة إلى تنسيق Cammelcase مثل Sayhello Check وما إلى ذلك.",
|
||||
acceptedStatusCodesDescription: "حدد رموز الحالة التي تعتبر استجابة ناجحة.",
|
||||
Maintenance: "صيانة",
|
||||
statusMaintenance: "صيانة",
|
||||
"Schedule maintenance": "جدولة الصيانة",
|
||||
"Affected Monitors": "الشاشات المتأثرة",
|
||||
"Pick Affected Monitors...": "اختيار الشاشات المتأثرة ...",
|
||||
"Start of maintenance": "بداية الصيانة",
|
||||
"All Status Pages": "جميع صفحات الحالة",
|
||||
"Select status pages...": "حدد صفحات الحالة ...",
|
||||
recurringIntervalMessage: "ركض مرة واحدة كل يوم | قم بالتشغيل مرة واحدة كل يوم {0}",
|
||||
affectedMonitorsDescription: "حدد المراقبين المتأثرة بالصيانة الحالية",
|
||||
affectedStatusPages: "إظهار رسالة الصيانة هذه على صفحات الحالة المحددة",
|
||||
atLeastOneMonitor: "حدد شاشة واحدة على الأقل من المتأثرين",
|
||||
passwordNotMatchMsg: "كلمة المرور المتكررة لا تتطابق.",
|
||||
notificationDescription: "يجب تعيين الإخطارات إلى شاشة للعمل.",
|
||||
keywordDescription: "ابحث في الكلمة الرئيسية في استجابة HTML العادية أو JSON. البحث حساس للحالة.",
|
||||
pauseDashboardHome: "وقفة",
|
||||
deleteMonitorMsg: "هل أنت متأكد من حذف هذا الشاشة؟",
|
||||
deleteMaintenanceMsg: "هل أنت متأكد من حذف هذه الصيانة؟",
|
||||
deleteNotificationMsg: "هل أنت متأكد من حذف هذا الإشعار لجميع الشاشات؟",
|
||||
dnsPortDescription: "منفذ خادم DNS. الافتراضيات إلى 53. يمكنك تغيير المنفذ في أي وقت.",
|
||||
resolverserverDescription: "CloudFlare هو الخادم الافتراضي. يمكنك تغيير خادم المحوّل في أي وقت.",
|
||||
rrtypeDescription: "حدد نوع RR الذي تريد مراقبته",
|
||||
pauseMonitorMsg: "هل أنت متأكد من أن تتوقف مؤقتًا؟",
|
||||
enableDefaultNotificationDescription: "سيتم تمكين هذا الإشعار افتراضيًا للشاشات الجديدة. لا يزال بإمكانك تعطيل الإخطار بشكل منفصل لكل شاشة.",
|
||||
clearEventsMsg: "هل أنت متأكد من حذف جميع الأحداث لهذا الشاشة؟",
|
||||
clearHeartbeatsMsg: "هل أنت متأكد من حذف جميع دقات القلب لهذا الشاشة؟",
|
||||
confirmClearStatisticsMsg: "هل أنت متأكد من أنك تريد حذف جميع الإحصائيات؟",
|
||||
importHandleDescription: "اختر 'تخطي موجود' إذا كنت تريد تخطي كل شاشة أو إشعار بنفس الاسم. 'الكتابة فوق' سوف يحذف كل شاشة وإخطار موجود.",
|
||||
confirmImportMsg: "هل أنت متأكد من أنك تريد استيراد النسخ الاحتياطي؟ يرجى التحقق من أنك حددت خيار الاستيراد الصحيح.",
|
||||
twoFAVerifyLabel: "الرجاء إدخال الرمز المميز الخاص بك للتحقق من 2FA",
|
||||
tokenValidSettingsMsg: "الرمز المميز صالح! يمكنك الآن حفظ إعدادات 2FA.",
|
||||
confirmEnableTwoFAMsg: "هل أنت متأكد من أنك تريد تمكين 2FA؟",
|
||||
confirmDisableTwoFAMsg: "هل أنت متأكد من أنك تريد تعطيل 2FA؟",
|
||||
Settings: "إعدادات",
|
||||
Dashboard: "لوحة التحكم",
|
||||
"New Update": "تحديث جديد",
|
||||
Language: "لغة",
|
||||
Appearance: "مظهر",
|
||||
Theme: "سمة",
|
||||
General: "عام",
|
||||
"Primary Base URL": "عنوان URL الأساسي",
|
||||
Version: "الإصدار",
|
||||
"Check Update On GitHub": "تحقق من التحديث على GitHub",
|
||||
List: "قائمة",
|
||||
Add: "يضيف",
|
||||
"Add New Monitor": "أضف شاشة جديدة",
|
||||
"Quick Stats": "إحصائيات سريعة",
|
||||
Up: "فوق",
|
||||
Down: "أسفل",
|
||||
Pending: "قيد الانتظار",
|
||||
Unknown: "غير معرّف",
|
||||
Pause: "إيقاف مؤقت",
|
||||
Name: "الاسم",
|
||||
Status: "الحالة",
|
||||
DateTime: "الوقت والتاريخ",
|
||||
Message: "الرسالة",
|
||||
"No important events": "لا توجد أحداث مهمة",
|
||||
Resume: "استمرار",
|
||||
Edit: "تعديل",
|
||||
Delete: "حذف",
|
||||
Current: "حالي",
|
||||
Uptime: "مدة التشغيل",
|
||||
"Cert Exp.": "تصدير الشهادة",
|
||||
Monitor: "مراقب | مراقبات",
|
||||
day: "يوم | أيام",
|
||||
"-day": "-يوم",
|
||||
hour: "ساعة",
|
||||
"-hour": "-ساعة",
|
||||
Response: "استجاية",
|
||||
Ping: "بينغ",
|
||||
"Monitor Type": "نوع المراقب",
|
||||
Keyword: "كلمة مفتاحية",
|
||||
"Friendly Name": "اسم معروف",
|
||||
URL: "عنوان URL",
|
||||
Hostname: "اسم المضيف",
|
||||
Port: "المنفذ",
|
||||
"Heartbeat Interval": "فاصل نبضات القلب",
|
||||
Retries: "يحاول مجدداً",
|
||||
"Heartbeat Retry Interval": "الفاصل الزمني لإعادة محاكمة نبضات القلب",
|
||||
"Resend Notification if Down X times consequently": "إعادة تقديم الإخطار إذا انخفض x مرات بالتالي",
|
||||
Advanced: "متقدم",
|
||||
"Upside Down Mode": "وضع أسفل أسفل",
|
||||
"Max. Redirects": "الأعلى. إعادة التوجيه",
|
||||
"Accepted Status Codes": "رموز الحالة المقبولة",
|
||||
"Push URL": "دفع عنوان URL",
|
||||
needPushEvery: "يجب عليك استدعاء عنوان URL هذا كل ثانية.",
|
||||
pushOptionalParams: "المعلمات الاختيارية",
|
||||
Save: "يحفظ",
|
||||
Notifications: "إشعارات",
|
||||
"Not available, please setup.": "غير متوفر من فضلك الإعداد.",
|
||||
"Setup Notification": "إشعار الإعداد",
|
||||
Light: "نور",
|
||||
Dark: "داكن",
|
||||
Auto: "آلي",
|
||||
"Theme - Heartbeat Bar": "موضوع - بار نبضات",
|
||||
Normal: "طبيعي",
|
||||
Bottom: "الأسفل",
|
||||
None: "لا أحد",
|
||||
Timezone: "وحدة زمنية",
|
||||
"Search Engine Visibility": "محرك بحث الرؤية",
|
||||
"Allow indexing": "السماح الفهرسة",
|
||||
"Discourage search engines from indexing site": "تثبيط محركات البحث من موقع الفهرسة",
|
||||
"Change Password": "غير كلمة السر",
|
||||
"Current Password": "كلمة المرور الحالي",
|
||||
"New Password": "كلمة سر جديدة",
|
||||
"Repeat New Password": "كرر كلمة المرور الجديدة",
|
||||
"Update Password": "تطوير كلمة السر",
|
||||
"Disable Auth": "تعطيل المصادقة",
|
||||
"Enable Auth": "تمكين المصادقة",
|
||||
"disableauth.message1": "هل أنت متأكد من أن <strong> تعطيل المصادقة </strong>؟",
|
||||
"disableauth.message2": "تم تصميمه للسيناريوهات <strong> حيث تنوي تنفيذ مصادقة الطرف الثالث </strong> أمام كوما في وقت التشغيل مثل CloudFlare Access Authelia أو آليات المصادقة الأخرى.",
|
||||
"Please use this option carefully!": "الرجاء استخدام هذا الخيار بعناية!",
|
||||
Logout: "تسجيل خروج",
|
||||
Leave: "غادر",
|
||||
"I understand, please disable": "أنا أفهم من فضلك تعطيل",
|
||||
Confirm: "يتأكد",
|
||||
Yes: "نعم",
|
||||
No: "رقم",
|
||||
Username: "اسم المستخدم",
|
||||
Password: "كلمة المرور",
|
||||
"Remember me": "تذكرنى",
|
||||
Login: "تسجيل الدخول",
|
||||
"No Monitors, please": "لا شاشات من فضلك",
|
||||
"add one": "أضف واحدا",
|
||||
"Notification Type": "نوع إعلام",
|
||||
Email: "بريد إلكتروني",
|
||||
Test: "امتحان",
|
||||
"Certificate Info": "معلومات الشهادة",
|
||||
"Resolver Server": "خادم Resolver",
|
||||
"Resource Record Type": "نوع سجل الموارد",
|
||||
"Last Result": "اخر نتيجة",
|
||||
"Create your admin account": "إنشاء حساب المسؤول الخاص بك",
|
||||
"Repeat Password": "اعد كلمة السر",
|
||||
"Import Backup": "استيراد النسخ الاحتياطي",
|
||||
"Export Backup": "النسخ الاحتياطي تصدير",
|
||||
Export: "يصدّر",
|
||||
Import: "يستورد",
|
||||
respTime: "resp. الوقت (MS)",
|
||||
notAvailableShort: "ن/أ",
|
||||
"Default enabled": "التمكين الافتراضي",
|
||||
"Apply on all existing monitors": "تنطبق على جميع الشاشات الحالية",
|
||||
Create: "خلق",
|
||||
"Clear Data": "امسح البيانات",
|
||||
Events: "الأحداث",
|
||||
Heartbeats: "نبضات القلب",
|
||||
"Auto Get": "الحصول على السيارات",
|
||||
backupDescription: "يمكنك النسخ الاحتياطي لجميع الشاشات والإشعارات في ملف JSON.",
|
||||
backupDescription2: "ملحوظة",
|
||||
backupDescription3: "يتم تضمين البيانات الحساسة مثل الرموز الإخطار في ملف التصدير ؛ يرجى تخزين التصدير بشكل آمن.",
|
||||
alertNoFile: "الرجاء تحديد ملف للاستيراد.",
|
||||
alertWrongFileType: "الرجاء تحديد ملف JSON.",
|
||||
"Clear all statistics": "مسح جميع الإحصاءات",
|
||||
"Skip existing": "تخطي الموجود",
|
||||
Overwrite: "الكتابة فوق",
|
||||
Options: "خيارات",
|
||||
"Keep both": "احتفظ بكليهما",
|
||||
"Verify Token": "تحقق من الرمز المميز",
|
||||
"Setup 2FA": "الإعداد 2FA",
|
||||
"Enable 2FA": "تمكين 2FA",
|
||||
"Disable 2FA": "تعطيل 2FA",
|
||||
"2FA Settings": "2FA إعدادات",
|
||||
"Two Factor Authentication": "توثيق ذو عاملين",
|
||||
Active: "نشيط",
|
||||
Inactive: "غير نشط",
|
||||
Token: "رمز",
|
||||
"Show URI": "أظهر URI",
|
||||
Tags: "العلامات",
|
||||
"Add New below or Select...": "أضف جديدًا أدناه أو حدد ...",
|
||||
"Tag with this name already exist.": "علامة مع هذا الاسم موجود بالفعل.",
|
||||
"Tag with this value already exist.": "علامة مع هذه القيمة موجودة بالفعل.",
|
||||
color: "اللون",
|
||||
"value (optional)": "القيمة (اختياري)",
|
||||
Gray: "رمادي",
|
||||
Red: "أحمر",
|
||||
Orange: "البرتقالي",
|
||||
Green: "لون أخضر",
|
||||
Blue: "أزرق",
|
||||
Indigo: "النيلي",
|
||||
Purple: "نفسجي",
|
||||
Pink: "لون القرنفل",
|
||||
Custom: "العادة",
|
||||
"Search...": "يبحث...",
|
||||
"Avg. Ping": "متوسط. بينغ",
|
||||
"Avg. Response": "متوسط. إجابة",
|
||||
"Entry Page": "صفحة الدخول",
|
||||
statusPageNothing: "لا شيء هنا الرجاء إضافة مجموعة أو شاشة.",
|
||||
"No Services": "لا توجد خدمات",
|
||||
"All Systems Operational": "جميع الأنظمة التشغيلية",
|
||||
"Partially Degraded Service": "الخدمة المتدهورة جزئيا",
|
||||
"Degraded Service": "خدمة متدهورة",
|
||||
"Add Group": "أضف مجموعة",
|
||||
"Add a monitor": "إضافة شاشة",
|
||||
"Edit Status Page": "تحرير صفحة الحالة",
|
||||
"Go to Dashboard": "الذهاب إلى لوحة القيادة",
|
||||
"Status Page": "صفحة الحالة",
|
||||
"Status Pages": "صفحات الحالة",
|
||||
defaultNotificationName: "تنبيه {الإخطار} ({number})",
|
||||
here: "هنا",
|
||||
Required: "مطلوب",
|
||||
telegram: "برقية",
|
||||
"ZohoCliq": "Zohocliq",
|
||||
"Bot Token": "رمز الروبوت",
|
||||
wayToGetTelegramToken: "يمكنك الحصول على رمز من {0}.",
|
||||
"Chat ID": "معرف الدردشة",
|
||||
supportTelegramChatID: "دعم الدردشة المباشرة / معرف الدردشة للقناة",
|
||||
wayToGetTelegramChatID: "يمكنك الحصول على معرف الدردشة الخاص بك عن طريق إرسال رسالة إلى الروبوت والانتقال إلى عنوان URL هذا لعرض Chat_id",
|
||||
"YOUR BOT TOKEN HERE": "رمز الروبوت الخاص بك هنا",
|
||||
chatIDNotFound: "لم يتم العثور على معرف الدردشة ؛ الرجاء إرسال رسالة إلى هذا الروبوت أولاً",
|
||||
webhook: "webhook",
|
||||
"Post URL": "بعد عنوان URL",
|
||||
"Content Type": "نوع المحتوى",
|
||||
webhookJsonDesc: "{0} مفيد لأي خوادم HTTP الحديثة مثل Express.js",
|
||||
webhookFormDataDesc: "{multipart} مفيد لـ PHP. سيحتاج JSON إلى تحليل {decodefunction}",
|
||||
webhookAdditionalHeadersTitle: "رؤوس إضافية",
|
||||
webhookAdditionalHeadersDesc: "يحدد رؤوس إضافية مرسلة مع webhook.",
|
||||
smtp: "البريد الإلكتروني (SMTP)",
|
||||
secureOptionNone: "لا شيء / startTls (25 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "تجاهل خطأ TLS",
|
||||
"From Email": "من البريد الإلكترونى",
|
||||
emailCustomSubject: "موضوع مخصص",
|
||||
"To Email": "للبريد الإلكتروني",
|
||||
smtpCC: "نسخة",
|
||||
smtpBCC: "BCC",
|
||||
discord: "خلاف",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "يمكنك الحصول على هذا عن طريق الانتقال إلى إعدادات الخادم -> التكامل -> إنشاء WebHook",
|
||||
"Bot Display Name": "اسم عرض الروبوت",
|
||||
"Prefix Custom Message": "بادئة رسالة مخصصة",
|
||||
"Hello @everyone is...": "مرحبًا {'@'} الجميع ...",
|
||||
teams: "فرق Microsoft",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "يمكنك معرفة كيفية إنشاء عنوان URL webhook {0}.",
|
||||
wayToGetZohoCliqURL: "يمكنك معرفة كيفية إنشاء عنوان URL webhook {0}.",
|
||||
signal: "الإشارة",
|
||||
Number: "رقم",
|
||||
Recipients: "المستلمين",
|
||||
needSignalAPI: "تحتاج إلى وجود عميل إشارة مع REST API.",
|
||||
wayToCheckSignalURL: "يمكنك التحقق من عنوان URL هذا لعرض كيفية إعداد واحد",
|
||||
signalImportant: "مهم",
|
||||
gotify: "gotify",
|
||||
"Application Token": "رمز التطبيق",
|
||||
"Server URL": "عنوان URL الخادم",
|
||||
Priority: "أولوية",
|
||||
slack: "تثاقل",
|
||||
"Icon Emoji": "أيقونة الرموز التعبيرية",
|
||||
"Channel Name": "اسم القناة",
|
||||
"Uptime Kuma URL": "UPTIME KUMA URL",
|
||||
aboutWebhooks: "مزيد من المعلومات حول Webhooks ON",
|
||||
aboutChannelName: "أدخل اسم القناة في حقل اسم القناة {0} إذا كنت تريد تجاوز قناة WebHook. السابق",
|
||||
aboutKumaURL: "إذا تركت حقل URL في وقت التشغيل KUMA فارغًا ، فسيتم افتراضيًا إلى صفحة GitHub Project.",
|
||||
emojiCheatSheet: "ورقة الغش في الرموز التعبيرية",
|
||||
"rocket.chat": "صاروخ",
|
||||
pushover: "مهمة سهلة",
|
||||
pushy: "انتهازي",
|
||||
PushByTechulus: "دفع بواسطة Techulus",
|
||||
octopush: "أوكتوبوش",
|
||||
promosms: "الترويجيات",
|
||||
clicksendsms: "نقرات SMS",
|
||||
lunasea: "لوناسيا",
|
||||
apprise: "إبلاغ (دعم 50+ خدمات الإخطار)",
|
||||
GoogleChat: "دردشة Google",
|
||||
pushbullet: "حماس",
|
||||
Kook: "كووك",
|
||||
wayToGetKookBotToken: "قم بإنشاء تطبيق واحصل على رمز الروبوت الخاص بك على {0}",
|
||||
wayToGetKookGuildID: "قم بتشغيل 'وضع المطور' في إعداد Kook وانقر بزر الماوس الأيمن على النقابة للحصول على معرفه",
|
||||
"Guild ID": "معرف النقابة",
|
||||
line: "خط",
|
||||
mattermost: "المادة",
|
||||
"User Key": "مفتاح المستخدم",
|
||||
Device: "جهاز",
|
||||
"Message Title": "عنوان الرسالة",
|
||||
"Notification Sound": "صوت الإشعار",
|
||||
"More info on": "مزيد من المعلومات حول",
|
||||
pushoverDesc1: "أولوية الطوارئ (2) لها مهلة افتراضية 30 ثانية بين إعادة المحاولة وستنتهي صلاحيتها بعد ساعة واحدة.",
|
||||
pushoverDesc2: "إذا كنت ترغب في إرسال إشعارات إلى أجهزة مختلفة ، قم بملء حقل الجهاز.",
|
||||
"SMS Type": "نوع الرسائل القصيرة",
|
||||
octopushTypePremium: "قسط (سريع - موصى به للتنبيه)",
|
||||
octopushTypeLowCost: "التكلفة المنخفضة (بطيئة - تم حظرها أحيانًا بواسطة المشغل)",
|
||||
checkPrice: "تحقق من الأسعار {0}",
|
||||
apiCredentials: "بيانات اعتماد API",
|
||||
octopushLegacyHint: "هل تستخدم الإصدار القديم من Octopush (2011-2020) أو الإصدار الجديد؟",
|
||||
"Check octopush prices": "تحقق من أسعار Octopush {0}.",
|
||||
octopushPhoneNumber: "رقم الهاتف (تنسيق intl على سبيل المثال",
|
||||
octopushSMSSender: "اسم مرسل الرسائل القصيرة",
|
||||
"LunaSea Device ID": "معرف جهاز Lunasea",
|
||||
"Apprise URL": "إبلاغ عنوان URL",
|
||||
"Example": "مثال",
|
||||
"Read more:": "{0} :قراءة المزيد",
|
||||
"Status:": "{0} :حالة",
|
||||
"Read more": "قراءة المزيد",
|
||||
appriseInstalled: "تم تثبيت Prosise.",
|
||||
appriseNotInstalled: "الإبرام غير مثبت. {0}",
|
||||
"Access Token": "رمز وصول",
|
||||
"Channel access token": "قناة الوصول إلى الرمز",
|
||||
"Line Developers Console": "تحكم المطورين",
|
||||
lineDevConsoleTo: "وحدة المطورين Line Console - {0}",
|
||||
"Basic Settings": "الإعدادات الأساسية",
|
||||
"User ID": "معرف المستخدم",
|
||||
"Messaging API": "واجهة برمجة تطبيقات المراسلة",
|
||||
wayToGetLineChannelToken: "قم أولاً بالوصول إلى {0} إنشاء مزود وقناة (واجهة برمجة تطبيقات المراسلة) ، ثم يمكنك الحصول على رمز الوصول إلى القناة ومعرف المستخدم من عناصر القائمة المذكورة أعلاه.",
|
||||
"Icon URL": "url url icon",
|
||||
aboutIconURL: "يمكنك توفير رابط لصورة في \"Icon URL\" لتجاوز صورة الملف الشخصي الافتراضي. لن يتم استخدامه إذا تم تعيين رمز رمز رمز.",
|
||||
aboutMattermostChannelName: "يمكنك تجاوز القناة الافتراضية التي تنشرها WebHook من خلال إدخال اسم القناة في \"Channel Name\" الحقل. يجب تمكين هذا في إعدادات Webhook Mattern. السابق",
|
||||
matrix: "مصفوفة",
|
||||
promosmsTypeEco: "SMS Eco - رخيصة ولكن بطيئة وغالبًا ما تكون محملة. يقتصر فقط على المستفيدين البولنديين.",
|
||||
promosmsTypeFlash: "SMS Flash - سيتم عرض الرسالة تلقائيًا على جهاز المستلم. يقتصر فقط على المستفيدين البولنديين.",
|
||||
promosmsTypeFull: "SMS Full - Tier Premium SMS يمكنك استخدام اسم المرسل الخاص بك (تحتاج إلى تسجيل الاسم أولاً). موثوقة للتنبيهات.",
|
||||
promosmsTypeSpeed: "سرعة الرسائل القصيرة - أولوية قصوى في النظام. سريع وموثوق للغاية ولكنه مكلف (حوالي مرتين من الرسائل القصيرة السعر الكامل).",
|
||||
promosmsPhoneNumber: "رقم الهاتف (للمستلم البولندي ، يمكنك تخطي رموز المنطقة)",
|
||||
promosmsSMSSender: "اسم مرسل الرسائل القصيرة",
|
||||
promosmsAllowLongSMS: "السماح الرسائل القصيرة الطويلة",
|
||||
"Feishu WebHookUrl": "Feishu Webhookurl",
|
||||
matrixHomeserverURL: "عنوان URL HomeServer (مع HTTP (S)",
|
||||
"Internal Room Id": "معرف الغرفة الداخلية",
|
||||
matrixDesc1: "يمكنك العثور على معرف الغرفة الداخلي من خلال البحث في القسم المتقدم من إعدادات الغرفة في عميل Matrix الخاص بك. يجب أن تبدو مثل! QMDRCPUIFLWSFJXYE6",
|
||||
matrixDesc2: "يوصى بشدة بإنشاء مستخدم جديد ولا تستخدم رمز الوصول إلى مستخدم Matrix الخاص بك لأنه سيتيح الوصول الكامل إلى حسابك وجميع الغرف التي انضمت إليها. بدلاً من ذلك ، قم بإنشاء مستخدم جديد ودعوته فقط إلى الغرفة التي تريد تلقيها الإشعار فيها. يمكنك الحصول على رمز الوصول عن طريق تشغيل {0}",
|
||||
Method: "طريقة",
|
||||
Body: "الجسم",
|
||||
Headers: "الرؤوس",
|
||||
PushUrl: "دفع عنوان URL",
|
||||
HeadersInvalidFormat: "رؤوس الطلبات غير صالحة JSON",
|
||||
BodyInvalidFormat: "هيئة الطلب غير صالحة JSON",
|
||||
"Monitor History": "مراقبة التاريخ",
|
||||
clearDataOlderThan: "الحفاظ على بيانات سجل المراقبة للأيام {0}.",
|
||||
PasswordsDoNotMatch: "كلمة المرور غير مطابقة.",
|
||||
records: "السجلات",
|
||||
"One record": "سجل واحد",
|
||||
steamApiKeyDescription: "لمراقبة خادم لعبة Steam ، تحتاج إلى مفتاح Steam Web-API. يمكنك تسجيل مفتاح API الخاص بك هنا",
|
||||
"Current User": "المستخدم الحالي",
|
||||
topic: "عنوان",
|
||||
topicExplanation: "موضوع MQTT لرصد",
|
||||
successMessage: "نجاح رسالة",
|
||||
successMessageExplanation: "رسالة MQTT التي ستعتبر نجاحًا",
|
||||
recent: "الأخيرة",
|
||||
Done: "فعله",
|
||||
Info: "معلومات",
|
||||
Security: "حماية",
|
||||
"Steam API Key": "مفتاح API Steam",
|
||||
"Shrink Database": "تقلص قاعدة البيانات",
|
||||
"Pick a RR-Type...": "اختر نوع RR ...",
|
||||
"Pick Accepted Status Codes...": "اختيار رموز الحالة المقبولة ...",
|
||||
Default: "تقصير",
|
||||
"HTTP Options": "خيارات HTTP",
|
||||
"Create Incident": "إنشاء حادث",
|
||||
Title: "لقب",
|
||||
Content: "المحتوى",
|
||||
Style: "أسلوب",
|
||||
info: "معلومات",
|
||||
warning: "تحذير",
|
||||
danger: "خطر",
|
||||
error: "خطأ",
|
||||
critical: "شديد الأهمية",
|
||||
primary: "الأولية",
|
||||
light: "نور",
|
||||
dark: "ظلام",
|
||||
Post: "بريد",
|
||||
"Please input title and content": "الرجاء إدخال العنوان والمحتوى",
|
||||
Created: "مخلوق",
|
||||
"Last Updated": "التحديث الاخير",
|
||||
Unpin: "إلغاء",
|
||||
"Switch to Light Theme": "التبديل إلى موضوع الضوء",
|
||||
"Switch to Dark Theme": "التبديل إلى موضوع الظلام",
|
||||
"Show Tags": "أضهر العلامات",
|
||||
"Hide Tags": "إخفاء العلامات",
|
||||
Description: "وصف",
|
||||
"No monitors available.": "لا شاشات المتاحة.",
|
||||
"Add one": "أضف واحدا",
|
||||
"No Monitors": "لا شاشات",
|
||||
"Untitled Group": "مجموعة بلا عنوان",
|
||||
Services: "خدمات",
|
||||
Discard: "تجاهل",
|
||||
Cancel: "يلغي",
|
||||
"Powered by": "مشغل بواسطة",
|
||||
shrinkDatabaseDescription: "تشغيل فراغ قاعدة البيانات لـ SQLite. إذا تم إنشاء قاعدة البيانات الخاصة بك بعد تمكين 1.10.0 AUTO_VACUUM بالفعل ولا يلزم هذا الإجراء.",
|
||||
serwersms: "Serwersms.pl",
|
||||
serwersmsAPIUser: "اسم مستخدم API (بما في ذلك بادئة WebAPI_)",
|
||||
serwersmsAPIPassword: "كلمة مرور API",
|
||||
serwersmsPhoneNumber: "رقم الهاتف",
|
||||
serwersmsSenderName: "اسم مرسل الرسائل القصيرة (مسجل عبر بوابة العملاء)",
|
||||
smseagle: "smseagle",
|
||||
smseagleTo: "أرقام الهواتف)",
|
||||
smseagleGroup: "اسم مجموعة كتب الهاتف (S)",
|
||||
smseagleContact: "كتاب الاتصال اسم (S)",
|
||||
smseagleRecipientType: "نوع المستلم",
|
||||
smseagleRecipient: "المتلقي (المتلقيين) (يجب فصل المتعددة مع فاصلة)",
|
||||
smseagleToken: "API وصول الرمز المميز",
|
||||
smseagleUrl: "عنوان URL لجهاز SMSEGLE الخاص بك",
|
||||
smseagleEncoding: "إرسال Unicode",
|
||||
smseaglePriority: "أولوية الرسالة (0-9 افتراضي = 0)",
|
||||
stackfield: "Stackfield",
|
||||
Customize: "يعدل أو يكيف",
|
||||
"Custom Footer": "تذييل مخصص",
|
||||
"Custom CSS": "لغة تنسيق ويب حسب الطلب",
|
||||
smtpDkimSettings: "إعدادات DKIM",
|
||||
smtpDkimDesc: "يرجى الرجوع إلى Nodemailer dkim {0} للاستخدام.",
|
||||
documentation: "توثيق",
|
||||
smtpDkimDomain: "اسم النطاق",
|
||||
smtpDkimKeySelector: "المحدد الرئيسي",
|
||||
smtpDkimPrivateKey: "مفتاح سري",
|
||||
smtpDkimHashAlgo: "خوارزمية التجزئة (اختياري)",
|
||||
smtpDkimheaderFieldNames: "مفاتيح الرأس للتوقيع (اختياري)",
|
||||
smtpDkimskipFields: "مفاتيح الرأس لا توقيع (اختياري)",
|
||||
wayToGetPagerDutyKey: "يمكنك الحصول على هذا عن طريق الانتقال إلى الخدمة -> دليل الخدمة -> (حدد خدمة) -> تكامل -> إضافة التكامل. هنا يمكنك البحث عن \"Events API V2\". مزيد من المعلومات {0}",
|
||||
"Integration Key": "مفتاح التكامل",
|
||||
"Integration URL": "URL تكامل",
|
||||
"Auto resolve or acknowledged": "حل السيارات أو الاعتراف به",
|
||||
"do nothing": "لا تفعل شيئا",
|
||||
"auto acknowledged": "اعترف السيارات",
|
||||
"auto resolve": "عزم السيارات",
|
||||
gorush: "جورش",
|
||||
alerta: "أليتا",
|
||||
alertaApiEndpoint: "نقطة نهاية API",
|
||||
alertaEnvironment: "بيئة",
|
||||
alertaApiKey: "مفتاح API",
|
||||
alertaAlertState: "حالة التنبيه",
|
||||
alertaRecoverState: "استعادة الدولة",
|
||||
deleteStatusPageMsg: "هل أنت متأكد من حذف صفحة الحالة هذه؟",
|
||||
Proxies: "وكلاء",
|
||||
default: "تقصير",
|
||||
enabled: "تمكين",
|
||||
setAsDefault: "تعيين كافتراضي",
|
||||
deleteProxyMsg: "هل أنت متأكد من حذف هذا الوكيل لجميع الشاشات؟",
|
||||
proxyDescription: "يجب تعيين الوكلاء إلى شاشة للعمل.",
|
||||
enableProxyDescription: "لن يؤثر هذا الوكيل على طلبات الشاشة حتى يتم تنشيطه. يمكنك التحكم مؤقتًا في تعطيل الوكيل من جميع الشاشات حسب حالة التنشيط.",
|
||||
setAsDefaultProxyDescription: "سيتم تمكين هذا الوكيل افتراضيًا للشاشات الجديدة. لا يزال بإمكانك تعطيل الوكيل بشكل منفصل لكل شاشة.",
|
||||
"Certificate Chain": "سلسلة الشهادة",
|
||||
Valid: "صالح",
|
||||
Invalid: "غير صالح",
|
||||
AccessKeyId: "معرف AccessKey",
|
||||
SecretAccessKey: "Accesskey Secret",
|
||||
PhoneNumbers: "أرقام الهواتف",
|
||||
TemplateCode: "TemplateCode",
|
||||
SignName: "اسم تسجيل الدخول",
|
||||
"Sms template must contain parameters: ": "يجب أن يحتوي قالب الرسائل القصيرة على معلمات:",
|
||||
"Bark Endpoint": "نقطة نهاية اللحاء",
|
||||
"Bark Group": "مجموعة اللحاء",
|
||||
"Bark Sound": "صوت اللحاء",
|
||||
WebHookUrl: "webhookurl",
|
||||
SecretKey: "Secretkey",
|
||||
"For safety, must use secret key": "للسلامة يجب استخدام المفتاح السري",
|
||||
"Device Token": "رمز الجهاز",
|
||||
Platform: "منصة",
|
||||
iOS: "iOS",
|
||||
Android: "ذكري المظهر",
|
||||
Huawei: "هواوي",
|
||||
High: "عالٍ",
|
||||
Retry: "إعادة المحاولة",
|
||||
Topic: "عنوان",
|
||||
"WeCom Bot Key": "WECOM BOT KEY",
|
||||
"Setup Proxy": "وكيل الإعداد",
|
||||
"Proxy Protocol": "بروتوكول الوكيل",
|
||||
"Proxy Server": "مخدم بروكسي",
|
||||
"Proxy server has authentication": "خادم الوكيل لديه مصادقة",
|
||||
User: "المستعمل",
|
||||
Installed: "المثبتة",
|
||||
"Not installed": "غير مثبت",
|
||||
Running: "جري",
|
||||
"Not running": "لا يعمل",
|
||||
"Remove Token": "إزالة الرمز المميز",
|
||||
Start: "بداية",
|
||||
Stop: "قف",
|
||||
"Uptime Kuma": "وقت التشغيل كوما",
|
||||
"Add New Status Page": "أضف صفحة حالة جديدة",
|
||||
Slug: "سبيكة",
|
||||
"Accept characters": "قبول الشخصيات",
|
||||
startOrEndWithOnly: "ابدأ أو ينتهي بـ {0} فقط",
|
||||
"No consecutive dashes": "لا شرطات متتالية",
|
||||
Next: "التالي",
|
||||
"The slug is already taken. Please choose another slug.": "تم أخذ سبيكة بالفعل. الرجاء اختيار سبيكة أخرى.",
|
||||
"No Proxy": "لا الوكيل",
|
||||
Authentication: "المصادقة",
|
||||
"HTTP Basic Auth": "HTTP الأساسي Auth",
|
||||
"New Status Page": "صفحة حالة جديدة",
|
||||
"Page Not Found": "الصفحة غير موجودة",
|
||||
"Reverse Proxy": "وكيل عكسي",
|
||||
Backup: "دعم",
|
||||
About: "عن",
|
||||
wayToGetCloudflaredURL: "(قم بتنزيل CloudFlared من {0})",
|
||||
cloudflareWebsite: "موقع CloudFlare",
|
||||
"Message:": ":رسالة",
|
||||
"Don't know how to get the token? Please read the guide": "لا أعرف كيف تحصل على الرمز المميز؟ يرجى قراءة الدليل",
|
||||
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "قد يضيع الاتصال الحالي إذا كنت تتصل حاليًا عبر نفق CloudFlare. هل أنت متأكد تريد إيقافها؟ اكتب كلمة المرور الحالية لتأكيدها.",
|
||||
"HTTP Headers": "رؤوس HTTP",
|
||||
"Trust Proxy": "الوكيل الثقة",
|
||||
"Other Software": "برامج أخرى",
|
||||
"For example: nginx, Apache and Traefik.": "على سبيل المثال: nginx و Apache و Traefik.",
|
||||
"Please read": "يرجى القراءة",
|
||||
"Subject": "موضوع",
|
||||
"Valid To": "صالحة ل",
|
||||
"Days Remaining": "الأيام المتبقية",
|
||||
"Issuer": "المصدر",
|
||||
"Fingerprint": "بصمة",
|
||||
"No status pages": "لا صفحات الحالة",
|
||||
"Domain Name Expiry Notification": "اسم النطاق إشعار انتهاء الصلاحية",
|
||||
Proxy: "الوكيل",
|
||||
"Date Created": "تاريخ الإنشاء",
|
||||
HomeAssistant: "مساعد المنزل",
|
||||
onebotHttpAddress: "OneBot HTTP عنوان",
|
||||
onebotMessageType: "نوع رسالة OneBot",
|
||||
onebotGroupMessage: "مجموعة",
|
||||
onebotPrivateMessage: "خاص",
|
||||
onebotUserOrGroupId: "معرف المجموعة/المستخدم",
|
||||
onebotSafetyTips: "للسلامة يجب ضبط الرمز المميز للوصول",
|
||||
"PushDeer Key": "مفتاح PushDeer",
|
||||
"Footer Text": "نص تذييل",
|
||||
"Show Powered By": "عرض مدعوم من قبل",
|
||||
"Domain Names": "أسماء المجال",
|
||||
signedInDisp: "وقعت في {0}",
|
||||
signedInDispDisabled: "معاق المصادقة.",
|
||||
RadiusSecret: "سر نصف القطر",
|
||||
RadiusSecretDescription: "السر المشترك بين العميل والخادم",
|
||||
RadiusCalledStationId: "يسمى معرف المحطة",
|
||||
RadiusCalledStationIdDescription: "معرف الجهاز المتصل",
|
||||
RadiusCallingStationId: "معرف محطة الاتصال",
|
||||
RadiusCallingStationIdDescription: "معرف جهاز الاتصال",
|
||||
"Certificate Expiry Notification": "إشعار انتهاء الصلاحية",
|
||||
"API Username": "اسم المستخدم API",
|
||||
"API Key": "مفتاح API",
|
||||
"Recipient Number": "رقم المستلم",
|
||||
"From Name/Number": "من الاسم/الرقم",
|
||||
"Leave blank to use a shared sender number.": "اترك فارغًا لاستخدام رقم المرسل المشترك.",
|
||||
"Octopush API Version": "إصدار Octopush API",
|
||||
"Legacy Octopush-DM": "Legacy Octopush-DM",
|
||||
endpoint: "نقطة النهاية",
|
||||
octopushAPIKey: "\"API key\" from HTTP API بيانات اعتماد في لوحة التحكم",
|
||||
octopushLogin: "\"Login\" من بيانات اعتماد API HTTP في لوحة التحكم",
|
||||
promosmsLogin: "اسم تسجيل الدخول API",
|
||||
promosmsPassword: "كلمة مرور API",
|
||||
"pushoversounds pushover": "سداد (افتراضي)",
|
||||
"pushoversounds bike": "دراجة هوائية",
|
||||
"pushoversounds bugle": "بوق",
|
||||
"pushoversounds cashregister": "ماكينة تسجيل المدفوعات النقدية",
|
||||
"pushoversounds classical": "كلاسيكي",
|
||||
"pushoversounds cosmic": "كونية",
|
||||
"pushoversounds falling": "هبوط",
|
||||
"pushoversounds gamelan": "Gamelan",
|
||||
"pushoversounds incoming": "واردة",
|
||||
"pushoversounds intermission": "استراحة",
|
||||
"pushoversounds magic": "سحر",
|
||||
"pushoversounds mechanical": "ميكانيكي",
|
||||
"pushoversounds pianobar": "شريط البيانو",
|
||||
"pushoversounds siren": "صفارة إنذار",
|
||||
"pushoversounds spacealarm": "إنذار الفضاء",
|
||||
"pushoversounds tugboat": "قارب السحب",
|
||||
"pushoversounds alien": "إنذار أجنبي (طويل)",
|
||||
"pushoversounds climb": "تسلق (طويل)",
|
||||
"pushoversounds persistent": "مستمر (طويل)",
|
||||
"pushoversounds echo": "صدى مهووس (طويل)",
|
||||
"pushoversounds updown": "صعودا (طويلة)",
|
||||
"pushoversounds vibrate": "يهتز فقط",
|
||||
"pushoversounds none": "لا شيء (صامت)",
|
||||
pushyAPIKey: "مفتاح API السري",
|
||||
pushyToken: "رمز الجهاز",
|
||||
"Show update if available": "عرض التحديث إذا كان ذلك متاحًا",
|
||||
"Also check beta release": "تحقق أيضًا من الإصدار التجريبي",
|
||||
"Using a Reverse Proxy?": "باستخدام وكيل عكسي؟",
|
||||
"Check how to config it for WebSocket": "تحقق من كيفية تكوينه لـ WebSocket",
|
||||
"Steam Game Server": "خادم لعبة البخار",
|
||||
"Most likely causes": "الأسباب المرجحة",
|
||||
"The resource is no longer available.": "لم يعد المورد متاحًا.",
|
||||
"There might be a typing error in the address.": "قد يكون هناك خطأ مطبعي في العنوان.",
|
||||
"What you can try": "ماذا تستطيع أن تجرب",
|
||||
"Retype the address.": "اعد كتابة العنوان.",
|
||||
"Go back to the previous page.": "عد للصفحة السابقة.",
|
||||
"Coming Soon": "قريبا",
|
||||
wayToGetClickSendSMSToken: "يمكنك الحصول على اسم مستخدم API ومفتاح API من {0}.",
|
||||
"Connection String": "سلسلة الاتصال",
|
||||
Query: "استفسار",
|
||||
settingsCertificateExpiry: "شهادة TLS انتهاء الصلاحية",
|
||||
certificationExpiryDescription: "شاشات HTTPS تضيء عندما تنتهي شهادة TLS في",
|
||||
"Setup Docker Host": "إعداد مضيف Docker",
|
||||
"Connection Type": "نوع الاتصال",
|
||||
"Docker Daemon": "Docker Daemon",
|
||||
deleteDockerHostMsg: "هل أنت متأكد من حذف مضيف Docker لجميع الشاشات؟",
|
||||
socket: "قابس كهرباء",
|
||||
tcp: "TCP / HTTP",
|
||||
"Docker Container": "حاوية Docker",
|
||||
"Container Name / ID": "اسم الحاوية / معرف",
|
||||
"Docker Host": "مضيف Docker",
|
||||
"Docker Hosts": "مضيفي Docker",
|
||||
"ntfy Topic": "موضوع ntfy",
|
||||
Domain: "اِختِصاص",
|
||||
Workstation: "محطة العمل",
|
||||
disableCloudflaredNoAuthMsg: "أنت في وضع مصادقة لا توجد كلمة مرور غير مطلوبة.",
|
||||
trustProxyDescription: "الثقة 'x-forward-*'. إذا كنت ترغب في الحصول على IP العميل الصحيح وكوما في الوقت المناسب مثل Nginx أو Apache ، فيجب عليك تمكين ذلك.",
|
||||
wayToGetLineNotifyToken: "يمكنك الحصول على رمز الوصول من {0}",
|
||||
Examples: "أمثلة",
|
||||
"Home Assistant URL": "Home Assistant URL",
|
||||
"Long-Lived Access Token": "الرمز المميز للوصول منذ فترة طويلة",
|
||||
"Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "يمكن إنشاء رمز الوصول منذ فترة طويلة عن طريق النقر على اسم ملف التعريف الخاص بك (أسفل اليسار) والتمرير إلى الأسفل ثم انقر فوق إنشاء الرمز المميز.",
|
||||
"Notification Service": "خدمة الإخطار",
|
||||
"default: notify all devices": "الافتراضي: إخطار جميع الأجهزة",
|
||||
"A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "يمكن العثور على قائمة بخدمات الإخطار في المساعد المنزلي ضمن \"Developer Tools > Services\" ابحث عن \"notification\" للعثور على اسم جهازك/هاتفك.",
|
||||
"Automations can optionally be triggered in Home Assistant": "يمكن أن يتم تشغيل الأتمتة اختياريًا في مساعد المنزل",
|
||||
"Trigger type": "نوع الزناد",
|
||||
"Event type": "نوع الحدث",
|
||||
"Event data": "بيانات الحدث",
|
||||
"Then choose an action, for example switch the scene to where an RGB light is red.": "ثم اختر إجراءً على سبيل المثال قم بتبديل المشهد إلى حيث يكون ضوء RGB أحمر.",
|
||||
"Frontend Version": "إصدار الواجهة الأمامية",
|
||||
"Frontend Version do not match backend version!": "إصدار Frontend لا يتطابق مع الإصدار الخلفي!",
|
||||
"Base URL": "عنوان URL الأساسي",
|
||||
goAlertInfo: "الهدف هو تطبيق مفتوح المصدر لجدولة الجدولة التلقائية والإشعارات (مثل الرسائل القصيرة أو المكالمات الصوتية). إشراك الشخص المناسب تلقائيًا بالطريقة الصحيحة وفي الوقت المناسب! {0}",
|
||||
goAlertIntegrationKeyInfo: "احصل على مفتاح تكامل API العام للخدمة في هذا التنسيق \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" عادةً قيمة المعلمة الرمزية لعنوان url المنسق.",
|
||||
goAlert: "المرمى",
|
||||
backupOutdatedWarning: "إهمال",
|
||||
backupRecommend: "يرجى النسخ الاحتياطي لحجم الصوت أو مجلد البيانات (./data/) مباشرة بدلاً من ذلك.",
|
||||
Optional: "اختياري",
|
||||
squadcast: "القاء فريقي",
|
||||
SendKey: "Sendkey",
|
||||
"SMSManager API Docs": "مستندات SMSManager API",
|
||||
"Gateway Type": "نوع البوابة",
|
||||
SMSManager: "smsmanager",
|
||||
"You can divide numbers with": "يمكنك تقسيم الأرقام مع",
|
||||
or: "أو",
|
||||
recurringInterval: "فترة",
|
||||
Recurring: "يتكرر",
|
||||
strategyManual: "نشط/غير نشط يدويًا",
|
||||
warningTimezone: "إنه يستخدم المنطقة الزمنية للخادم",
|
||||
weekdayShortMon: "الاثنين",
|
||||
weekdayShortTue: "الثلاثاء",
|
||||
weekdayShortWed: "تزوج",
|
||||
weekdayShortThu: "الخميس",
|
||||
weekdayShortFri: "الجمعة",
|
||||
weekdayShortSat: "جلس",
|
||||
weekdayShortSun: "شمس",
|
||||
dayOfWeek: "يوم من الأسبوع",
|
||||
dayOfMonth: "يوم من الشهر",
|
||||
lastDay: "بالأمس",
|
||||
lastDay1: "آخر يوم من الشهر",
|
||||
lastDay2: "الثاني في اليوم الأخير من الشهر",
|
||||
lastDay3: "الثالث في اليوم الأخير من الشهر",
|
||||
lastDay4: "الرابع في اليوم الأخير من الشهر",
|
||||
"No Maintenance": "لا صيانة",
|
||||
pauseMaintenanceMsg: "هل أنت متأكد من أن تتوقف مؤقتًا؟",
|
||||
"maintenanceStatus-under-maintenance": "تحت الصيانة",
|
||||
"maintenanceStatus-inactive": "غير نشط",
|
||||
"maintenanceStatus-scheduled": "المقرر",
|
||||
"maintenanceStatus-ended": "انتهى",
|
||||
"maintenanceStatus-unknown": "مجهول",
|
||||
"Display Timezone": "عرض المنطقة الزمنية",
|
||||
"Server Timezone": "المنطقة الزمنية الخادم",
|
||||
statusPageMaintenanceEndDate: "نهاية",
|
||||
IconUrl: "url url icon",
|
||||
"Enable DNS Cache": "تمكين ذاكرة التخزين المؤقت DNS",
|
||||
Enable: "يُمكَِن",
|
||||
Disable: "إبطال",
|
||||
dnsCacheDescription: "قد لا يعمل في بعض بيئات IPv6 تعطيله إذا واجهت أي مشكلات.",
|
||||
"Single Maintenance Window": "نافذة صيانة واحدة",
|
||||
"Maintenance Time Window of a Day": "نافذة وقت الصيانة لليوم",
|
||||
"Effective Date Range": "نطاق التاريخ السريع",
|
||||
"Schedule Maintenance": "جدولة الصيانة",
|
||||
"Date and Time": "التاريخ و الوقت",
|
||||
"DateTime Range": "نطاق DateTime",
|
||||
Strategy: "إستراتيجية",
|
||||
"Free Mobile User Identifier": "معرف مستخدم الهاتف المحمول المجاني",
|
||||
"Free Mobile API Key": "مفتاح واجهة برمجة تطبيقات مجانية للهاتف المحمول",
|
||||
"Enable TLS": "تمكين TLS",
|
||||
"Proto Service Name": "اسم خدمة البروتو",
|
||||
"Proto Method": "طريقة البروتو",
|
||||
"Proto Content": "محتوى proto",
|
||||
Economy: "اقتصاد",
|
||||
Lowcost: "تكلفة منخفضة",
|
||||
high: "عالي",
|
||||
"General Monitor Type": "نوع الشاشة العامة",
|
||||
"Passive Monitor Type": "نوع الشاشة السلبي",
|
||||
"Specific Monitor Type": "نوع شاشة محدد",
|
||||
dataRetentionTimeError: "يجب أن تكون فترة الاستبقاء 0 أو أكبر",
|
||||
infiniteRetention: "ضبط على 0 للاحتفاظ لا نهائي.",
|
||||
confirmDeleteTagMsg: "هل أنت متأكد من أنك تريد حذف هذه العلامة؟ لن يتم حذف الشاشات المرتبطة بهذه العلامة.",
|
||||
};
|
@ -8,12 +8,27 @@ export default {
|
||||
ignoreTLSError: "Ignorovat TLS/SSL chyby na HTTPS stránkách",
|
||||
upsideDownModeDescription: "Pomocí této možnosti změníte způsob vyhodnocování stavu. Pokud je služba dosažitelná, je NEDOSTUPNÁ.",
|
||||
maxRedirectDescription: "Maximální počet přesměrování, která se mají následovat. Nastavením hodnoty 0 zakážete přesměrování.",
|
||||
enableGRPCTls: "Umožnit odeslání gRPC žádosti během TLS spojení",
|
||||
grpcMethodDescription: "Název metody se převede do cammelCase formátu jako je sayHello, check, aj.",
|
||||
acceptedStatusCodesDescription: "Vyberte stavové kódy, které jsou považovány za úspěšnou odpověď.",
|
||||
Maintenance: "Údržba",
|
||||
statusMaintenance: "Údržba",
|
||||
"Schedule maintenance": "Naplánovat údržbu",
|
||||
"Affected Monitors": "Dotčené dohledy",
|
||||
"Pick Affected Monitors...": "Vyberte dotčené dohledy…",
|
||||
"Start of maintenance": "Zahájit údržbu",
|
||||
"All Status Pages": "Všechny stavové stránky",
|
||||
"Select status pages...": "Vyberte stavovou stránku…",
|
||||
recurringIntervalMessage: "Spustit jednou každý den | Spustit jednou každých {0} dní",
|
||||
affectedMonitorsDescription: "Vyberte dohledy, které budou ovlivněny touto údržbou",
|
||||
affectedStatusPages: "Zobrazit tuto zprávu o údržbě na vybraných stavových stránkách",
|
||||
atLeastOneMonitor: "Vyberte alespoň jeden dotčený dohled",
|
||||
passwordNotMatchMsg: "Hesla se neshodují",
|
||||
notificationDescription: "Pro zajištění funkčnosti oznámení je nutné jej přiřadit dohledu.",
|
||||
keywordDescription: "Vyhledat klíčové slovo v prosté odpovědi HTML nebo JSON. Při hledání se rozlišuje velikost písmen.",
|
||||
pauseDashboardHome: "Pozastavit",
|
||||
pauseDashboardHome: "Pozastaveno",
|
||||
deleteMonitorMsg: "Opravdu chcete odstranit tento dohled?",
|
||||
deleteMaintenanceMsg: "Opravdu chcete odstranit tuto údržbu?",
|
||||
deleteNotificationMsg: "Opravdu chcete odstranit toto oznámení pro všechny dohledy?",
|
||||
dnsPortDescription: "Port DNS serveru. Standardně běží na portu 53. V případě potřeby jej můžete kdykoli změnit.",
|
||||
resolverserverDescription: "Cloudflare je výchozí server. V případě potřeby můžete Resolver server kdykoli změnit.",
|
||||
@ -47,7 +62,7 @@ export default {
|
||||
Down: "Nedostupný",
|
||||
Pending: "Čekám",
|
||||
Unknown: "Neznámý",
|
||||
Pause: "Pozastaveno",
|
||||
Pause: "Pozastavit",
|
||||
Name: "Název",
|
||||
Status: "Stav",
|
||||
DateTime: "Časové razítko",
|
||||
@ -59,6 +74,7 @@ export default {
|
||||
Current: "Aktuální",
|
||||
Uptime: "Doba provozu",
|
||||
"Cert Exp.": "Platnost certifikátu",
|
||||
Monitor: "Dohled | Dohledy",
|
||||
day: "den | dny/í",
|
||||
"-day": "-dní",
|
||||
hour: "hodina",
|
||||
@ -175,6 +191,7 @@ export default {
|
||||
Indigo: "Indigo",
|
||||
Purple: "Purpurová",
|
||||
Pink: "Růžová",
|
||||
Custom: "Vlastní",
|
||||
"Search...": "Hledat…",
|
||||
"Avg. Ping": "Průměr Ping",
|
||||
"Avg. Response": "Průměr Odpověď",
|
||||
@ -194,6 +211,7 @@ export default {
|
||||
here: "sem",
|
||||
Required: "Vyžadováno",
|
||||
telegram: "Telegram",
|
||||
"ZohoCliq": "ZohoCliq",
|
||||
"Bot Token": "Token robota",
|
||||
wayToGetTelegramToken: "Token můžete získat od {0}.",
|
||||
"Chat ID": "ID chatu",
|
||||
@ -206,6 +224,8 @@ export default {
|
||||
"Content Type": "Typ obsahu",
|
||||
webhookJsonDesc: "{0} je vhodný pro všechny moderní servery HTTP, jako je Express.js",
|
||||
webhookFormDataDesc: "{multipart} je vhodné pro PHP. JSON bude nutné analyzovat prostřednictvím {decodeFunction}",
|
||||
webhookAdditionalHeadersTitle: "Dodatečné hlavičky",
|
||||
webhookAdditionalHeadersDesc: "Nastavte dodatečné hlavičky, které se odešlou společně s webhookem.",
|
||||
smtp: "E-mail (SMTP)",
|
||||
secureOptionNone: "Žádné / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
@ -223,7 +243,8 @@ export default {
|
||||
"Hello @everyone is...": "Dobrý den, {'@'}všichni jsou…",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "URL adresa webhooku",
|
||||
wayToGetTeamsURL: "Informace o tom, jak vytvořit URL adresu webhooku naleznete {0}.",
|
||||
wayToGetTeamsURL: "Informace o tom, jak vytvořit URL adresu webhooku naleznete na {0}.",
|
||||
wayToGetZohoCliqURL: "Informace o tom, jak vytvořit URL adresu webhooku naleznete na {0}.",
|
||||
signal: "Signal",
|
||||
Number: "Číslo",
|
||||
Recipients: "Příjemci",
|
||||
@ -253,6 +274,10 @@ export default {
|
||||
apprise: "Apprise (podpora více než 50 oznamovacích služeb)",
|
||||
GoogleChat: "Google Chat (pouze Google Workspace)",
|
||||
pushbullet: "Pushbullet",
|
||||
Kook: "Kook",
|
||||
wayToGetKookBotToken: "Aplikaci vytvoříte a token bota získáte na {0}",
|
||||
wayToGetKookGuildID: "V nastavení Kook aktivujte 'Vývojářský režim' a kliknutím pravým tlačítkem na guild získejte jeho ID",
|
||||
"Guild ID": "Guild ID",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "Klíč uživatele",
|
||||
@ -297,6 +322,7 @@ export default {
|
||||
promosmsTypeSpeed: "SMS SPEED – nejvyšší priorita v systému. Velmi rychlé a spolehlivé, ale nákladné (přibližně dvojnásobek ceny SMS FULL).",
|
||||
promosmsPhoneNumber: "Telefonní číslo (polští příjemci mohou vynechat telefonní předvolbu)",
|
||||
promosmsSMSSender: "Odesílatel SMS: Předem zaregistrovaný název nebo jeden z výchozích: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
promosmsAllowLongSMS: "Povolit dlouhé SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "URL adresa domácího serveru (s http(s):// a volitelně portem)",
|
||||
"Internal Room Id": "ID interní místnosti",
|
||||
@ -365,6 +391,16 @@ export default {
|
||||
serwersmsAPIPassword: "API heslo",
|
||||
serwersmsPhoneNumber: "Telefonní číslo",
|
||||
serwersmsSenderName: "Odesílatel SMS (registrováno prostřednictvím zákaznického portálu)",
|
||||
smseagle: "SMSEagle",
|
||||
smseagleTo: "Telefonní číslo(a)",
|
||||
smseagleGroup: "Název skupiny v adresáři",
|
||||
smseagleContact: "Název kontaktu v adresáři",
|
||||
smseagleRecipientType: "Typ příjemce",
|
||||
smseagleRecipient: "Příjemce(i) (více záznamů oddělte čárkou)",
|
||||
smseagleToken: "API přístupový token",
|
||||
smseagleUrl: "URL vašeho SMSEagle zařízení",
|
||||
smseagleEncoding: "Odeslat v Unicode",
|
||||
smseaglePriority: "Priorita zprávy (0-9, výchozí = 0)",
|
||||
"stackfield": "Stackfield",
|
||||
Customize: "Přizpůsobit",
|
||||
"Custom Footer": "Vlastní patička",
|
||||
@ -588,11 +624,11 @@ export default {
|
||||
"SMSManager API Docs": "SMSManager API Docs ",
|
||||
"Gateway Type": "Gateway Typ",
|
||||
SMSManager: "SMSManager",
|
||||
"You can divide numbers with": "Čísla můžete dělit pomocí",
|
||||
"You can divide numbers with": "Čísla můžete oddělit pomocí",
|
||||
"or": "nebo",
|
||||
recurringInterval: "Interval",
|
||||
"Recurring": "Opakující se",
|
||||
strategyManual: "Aktivní/Neaktivní Ručně",
|
||||
strategyManual: "Ruční spuštění/vypnutí",
|
||||
warningTimezone: "Používá se časové pásmo serveru",
|
||||
weekdayShortMon: "Po",
|
||||
weekdayShortTue: "Út",
|
||||
@ -608,8 +644,8 @@ export default {
|
||||
lastDay2: "2. poslední den v měsíci",
|
||||
lastDay3: "3. poslední den v měsíci",
|
||||
lastDay4: "4. poslední den v měsíci",
|
||||
"No Maintenance": "Žádna údržba",
|
||||
pauseMaintenanceMsg: "Jsi si jistý, že chceš pozastavit údržbu?",
|
||||
"No Maintenance": "Žádná údržba",
|
||||
pauseMaintenanceMsg: "Opravdu chcete pozastavit údržbu?",
|
||||
"maintenanceStatus-under-maintenance": "Údržba",
|
||||
"maintenanceStatus-inactive": "Neaktivní",
|
||||
"maintenanceStatus-scheduled": "Naplánováno",
|
||||
@ -622,5 +658,27 @@ export default {
|
||||
"Enable DNS Cache": "Povolit DNS Cache",
|
||||
"Enable": "Povolit",
|
||||
"Disable": "Zakázat",
|
||||
dnsCacheDescription: "V některých prostředích IPv6 nemusí fungovat. Pokud narazíte na nějaké problémy, vypněte jej.",
|
||||
dnsCacheDescription: "V některých IPv6 prostředích nemusí fungovat. Pokud narazíte na nějaké problémy, vypněte jej.",
|
||||
"Single Maintenance Window": "Konkrétní časové okno pro údržbu",
|
||||
"Maintenance Time Window of a Day": "Časové okno pro údržbu v daný den",
|
||||
"Effective Date Range": "Časové období",
|
||||
"Schedule Maintenance": "Naplánovat údržbu",
|
||||
"Date and Time": "Datum a čas",
|
||||
"DateTime Range": "Rozsah data a času",
|
||||
Strategy: "Strategie",
|
||||
"Free Mobile User Identifier": "Free Mobile User Identifier",
|
||||
"Free Mobile API Key": "Free Mobile API Key",
|
||||
"Enable TLS": "Povolit TLS",
|
||||
"Proto Service Name": "Proto Service Name",
|
||||
"Proto Method": "Proto Method",
|
||||
"Proto Content": "Proto Content",
|
||||
Economy: "Úsporná",
|
||||
Lowcost: "Nízkonákladová",
|
||||
high: "high",
|
||||
"General Monitor Type": "Obecný typ dohledu",
|
||||
"Passive Monitor Type": "Pasivní typ dohledu",
|
||||
"Specific Monitor Type": "Konkrétní typ dohledu",
|
||||
dataRetentionTimeError: "Doba pro uchování musí být větší nebo rovna 0",
|
||||
infiniteRetention: "Pro nekonečný záznam zadejte 0.",
|
||||
confirmDeleteTagMsg: "Opravdu chcete odstranit tento štíte? Provedením této akce nedojde k odstranění dohledů, které jej mají přiřazeny.",
|
||||
};
|
||||
|
@ -181,7 +181,7 @@ export default {
|
||||
"Add New below or Select...": "Add New below or Select...",
|
||||
"Tag with this name already exist.": "Tag with this name already exists.",
|
||||
"Tag with this value already exist.": "Tag with this value already exists.",
|
||||
color: "color",
|
||||
color: "Color",
|
||||
"value (optional)": "value (optional)",
|
||||
Gray: "Gray",
|
||||
Red: "Red",
|
||||
@ -322,6 +322,7 @@ export default {
|
||||
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",
|
||||
promosmsAllowLongSMS: "Allow long SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "Homeserver URL (with http(s):// and optionally port)",
|
||||
"Internal Room Id": "Internal Room ID",
|
||||
|
@ -209,7 +209,7 @@ export default {
|
||||
here: "ici",
|
||||
Required: "Requis",
|
||||
telegram: "Telegram",
|
||||
"ZohoCliq": "ZohoCliq",
|
||||
ZohoCliq: "ZohoCliq",
|
||||
"Bot Token": "Jeton du robot",
|
||||
wayToGetTelegramToken: "Vous pouvez obtenir un token depuis {0}.",
|
||||
"Chat ID": "Chat ID",
|
||||
@ -308,7 +308,7 @@ export default {
|
||||
lineDevConsoleTo: "Console développeurs Line - {0}",
|
||||
"Basic Settings": "Paramètres de base",
|
||||
"User ID": "Identifiant utilisateur",
|
||||
"Messaging API": "Messaging API", // Ne pas traduire, il s'agit du type de salon affiché sur la console développeurs Line
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "Premièrement accédez à {0}, créez un <i>provider</i> et définissez un type de salon à « Messaging API ». Vous pourrez alors avoir puis vous pourrez avoir le jeton d'accès du salon et l'identifiant utilisateur demandés.",
|
||||
"Icon URL": "URL vers l'icône",
|
||||
aboutIconURL: "Vous pouvez mettre un lien vers une image dans « URL vers l'icône » pour remplacer l'image de profil par défaut. Elle ne sera utilisé que si « Icône émoji » n'est pas défini.",
|
||||
@ -669,12 +669,16 @@ export default {
|
||||
"Proto Service Name": "Nom du service proto",
|
||||
"Proto Method": "Méthode Proto",
|
||||
"Proto Content": "Contenu proto",
|
||||
"Economy": "Économique",
|
||||
"Lowcost": "Faible coût",
|
||||
"high": "Haute",
|
||||
Economy: "Économique",
|
||||
Lowcost: "Faible coût",
|
||||
high: "Haute",
|
||||
"General Monitor Type": "Type de sonde générale",
|
||||
"Passive Monitor Type": "Type de sonde passive",
|
||||
"Specific Monitor Type": "Type de sonde spécifique",
|
||||
dataRetentionTimeError: "La durée de conservation doit être supérieure ou égale à 0",
|
||||
infiniteRetention: "Définissez la valeur à 0 pour une durée de conservation infinie.",
|
||||
Monitor: "Sonde | Sondes",
|
||||
Custom: "Personnalisé",
|
||||
confirmDeleteTagMsg: "Voulez-vous vraiment supprimer cette étiquettes ? Les moniteurs associés ne seront pas supprimés.",
|
||||
promosmsAllowLongSMS: "Autoriser les longs SMS",
|
||||
};
|
||||
|
@ -284,6 +284,7 @@ export default {
|
||||
promosmsTypeSpeed: "SMS SPEED - wysyłka priorytetowa, ma wszystkie zalety SMS FULL",
|
||||
promosmsPhoneNumber: "Numer odbiorcy",
|
||||
promosmsSMSSender: "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)",
|
||||
promosmsAllowLongSMS: "Zezwól na długie SMSy",
|
||||
"Primary Base URL": "Główny URL",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Powinieneś wywoływać ten URL co {0} sekund",
|
||||
|
@ -63,6 +63,12 @@
|
||||
</router-link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://github.com/louislam/uptime-kuma/wiki" class="dropdown-item" target="_blank">
|
||||
<font-awesome-icon icon="info-circle" /> {{ $t("Help") }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li v-if="$root.loggedIn && $root.socket.token !== 'autoLogin'">
|
||||
<button class="dropdown-item" @click="$root.logout">
|
||||
<font-awesome-icon icon="sign-out-alt" />
|
||||
|
@ -12,6 +12,11 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Convert value to UTC
|
||||
* @param {string | number | Date | dayjs.Dayjs} value
|
||||
* @returns {dayjs.Dayjs}
|
||||
*/
|
||||
toUTC(value) {
|
||||
return dayjs.tz(value, this.timezone).utc().format();
|
||||
},
|
||||
@ -34,6 +39,11 @@ export default {
|
||||
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
|
||||
/**
|
||||
* Get time for maintenance
|
||||
* @param {string | number | Date | dayjs.Dayjs} value
|
||||
* @returns {string}
|
||||
*/
|
||||
datetimeMaintenance(value) {
|
||||
const inputDate = new Date(value);
|
||||
const now = new Date(Date.now());
|
||||
|
@ -3,6 +3,7 @@ import { useToast } from "vue-toastification";
|
||||
import jwtDecode from "jwt-decode";
|
||||
import Favico from "favico.js";
|
||||
import dayjs from "dayjs";
|
||||
import { DOWN, MAINTENANCE, PENDING, UP } from "../util.ts";
|
||||
const toast = useToast();
|
||||
|
||||
let socket;
|
||||
@ -454,6 +455,10 @@ export default {
|
||||
socket.emit("getMonitorList", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get list of maintenances
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
getMaintenanceList(callback) {
|
||||
if (! callback) {
|
||||
callback = () => { };
|
||||
@ -470,22 +475,49 @@ export default {
|
||||
socket.emit("add", monitor, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a maintenace
|
||||
* @param {Object} maintenance
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
addMaintenance(maintenance, callback) {
|
||||
socket.emit("addMaintenance", maintenance, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add monitors to maintenance
|
||||
* @param {number} maintenanceID
|
||||
* @param {number[]} monitors
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
addMonitorMaintenance(maintenanceID, monitors, callback) {
|
||||
socket.emit("addMonitorMaintenance", maintenanceID, monitors, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add status page to maintenance
|
||||
* @param {number} maintenanceID
|
||||
* @param {number} statusPages
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
addMaintenanceStatusPage(maintenanceID, statusPages, callback) {
|
||||
socket.emit("addMaintenanceStatusPage", maintenanceID, statusPages, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get monitors affected by maintenance
|
||||
* @param {number} maintenanceID
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
getMonitorMaintenance(maintenanceID, callback) {
|
||||
socket.emit("getMonitorMaintenance", maintenanceID, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get status pages where maintenance is shown
|
||||
* @param {number} maintenanceID
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
getMaintenanceStatusPage(maintenanceID, callback) {
|
||||
socket.emit("getMaintenanceStatusPage", maintenanceID, callback);
|
||||
},
|
||||
@ -499,6 +531,11 @@ export default {
|
||||
socket.emit("deleteMonitor", monitorID, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete specified maintenance
|
||||
* @param {number} maintenanceID
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
deleteMaintenance(maintenanceID, callback) {
|
||||
socket.emit("deleteMaintenance", maintenanceID, callback);
|
||||
},
|
||||
@ -590,28 +627,28 @@ export default {
|
||||
for (let monitorID in this.lastHeartbeatList) {
|
||||
let lastHeartBeat = this.lastHeartbeatList[monitorID];
|
||||
|
||||
if (this.monitorList[monitorID] && this.monitorList[monitorID].maintenance) {
|
||||
result[monitorID] = {
|
||||
text: this.$t("statusMaintenance"),
|
||||
color: "maintenance",
|
||||
};
|
||||
} else if (! lastHeartBeat) {
|
||||
if (! lastHeartBeat) {
|
||||
result[monitorID] = unknown;
|
||||
} else if (lastHeartBeat.status === 1) {
|
||||
} else if (lastHeartBeat.status === UP) {
|
||||
result[monitorID] = {
|
||||
text: this.$t("Up"),
|
||||
color: "primary",
|
||||
};
|
||||
} else if (lastHeartBeat.status === 0) {
|
||||
} else if (lastHeartBeat.status === DOWN) {
|
||||
result[monitorID] = {
|
||||
text: this.$t("Down"),
|
||||
color: "danger",
|
||||
};
|
||||
} else if (lastHeartBeat.status === 2) {
|
||||
} else if (lastHeartBeat.status === PENDING) {
|
||||
result[monitorID] = {
|
||||
text: this.$t("Pending"),
|
||||
color: "warning",
|
||||
};
|
||||
} else if (lastHeartBeat.status === MAINTENANCE) {
|
||||
result[monitorID] = {
|
||||
text: this.$t("statusMaintenance"),
|
||||
color: "maintenance",
|
||||
};
|
||||
} else {
|
||||
result[monitorID] = unknown;
|
||||
}
|
||||
@ -633,17 +670,17 @@ export default {
|
||||
let beat = this.$root.lastHeartbeatList[monitorID];
|
||||
let monitor = this.$root.monitorList[monitorID];
|
||||
|
||||
if (monitor && monitor.maintenance) {
|
||||
result.maintenance++;
|
||||
} else if (monitor && ! monitor.active) {
|
||||
if (monitor && ! monitor.active) {
|
||||
result.pause++;
|
||||
} else if (beat) {
|
||||
if (beat.status === 1) {
|
||||
if (beat.status === UP) {
|
||||
result.up++;
|
||||
} else if (beat.status === 0) {
|
||||
} else if (beat.status === DOWN) {
|
||||
result.down++;
|
||||
} else if (beat.status === 2) {
|
||||
} else if (beat.status === PENDING) {
|
||||
result.up++;
|
||||
} else if (beat.status === MAINTENANCE) {
|
||||
result.maintenance++;
|
||||
} else {
|
||||
result.unknown++;
|
||||
}
|
||||
|
@ -356,6 +356,7 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
/** Initialise page */
|
||||
init() {
|
||||
this.affectedMonitors = [];
|
||||
this.selectedStatusPages = [];
|
||||
@ -414,6 +415,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/** Create new maintenance */
|
||||
async submit() {
|
||||
this.processing = true;
|
||||
|
||||
@ -458,6 +460,11 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add monitor to maintenance
|
||||
* @param {number} maintenanceID
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
async addMonitorMaintenance(maintenanceID, callback) {
|
||||
await this.$root.addMonitorMaintenance(maintenanceID, this.affectedMonitors, async (res) => {
|
||||
if (!res.ok) {
|
||||
@ -470,6 +477,11 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add status page to maintenance
|
||||
* @param {number} maintenanceID
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
async addMaintenanceStatusPage(maintenanceID, callback) {
|
||||
await this.$root.addMaintenanceStatusPage(maintenanceID, (this.showOnAllPages) ? this.selectedStatusPagesOptions : this.selectedStatusPages, async (res) => {
|
||||
if (!res.ok) {
|
||||
|
@ -111,7 +111,7 @@
|
||||
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius only -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'mqtt' || monitor.type === 'radius'" class="my-3">
|
||||
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
|
||||
<input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${ipRegexPattern}|${hostnameRegexPattern}`" required>
|
||||
<input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${monitor.type === 'mqtt' ? mqttIpOrHostnameRegexPattern : ipOrHostnameRegexPattern}`" required>
|
||||
</div>
|
||||
|
||||
<!-- Port -->
|
||||
@ -600,6 +600,7 @@ import DockerHostDialog from "../components/DockerHostDialog.vue";
|
||||
import ProxyDialog from "../components/ProxyDialog.vue";
|
||||
import TagsManager from "../components/TagsManager.vue";
|
||||
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } from "../util.ts";
|
||||
import { hostNameRegexPattern } from "../util-frontend";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
@ -624,11 +625,8 @@ export default {
|
||||
},
|
||||
acceptedStatusCodeOptions: [],
|
||||
dnsresolvetypeOptions: [],
|
||||
|
||||
// Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/
|
||||
ipRegexPattern: "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))",
|
||||
// Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
|
||||
hostnameRegexPattern: "^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$"
|
||||
ipOrHostnameRegexPattern: hostNameRegexPattern(),
|
||||
mqttIpOrHostnameRegexPattern: hostNameRegexPattern(true)
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -65,6 +65,7 @@ export default {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
/** Initialise page */
|
||||
init() {
|
||||
this.$root.getSocket().emit("getMonitorMaintenance", this.$route.params.id, (res) => {
|
||||
if (res.ok) {
|
||||
@ -83,10 +84,12 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Confirm deletion */
|
||||
deleteDialog() {
|
||||
this.$refs.confirmDelete.show();
|
||||
},
|
||||
|
||||
/** Delete maintenance after showing confirmation */
|
||||
deleteMaintenance() {
|
||||
this.$root.deleteMaintenance(this.maintenance.id, (res) => {
|
||||
if (res.ok) {
|
||||
|
@ -133,15 +133,25 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get maintenance URL
|
||||
* @param {number} id
|
||||
* @returns {string} Relative URL
|
||||
*/
|
||||
maintenanceURL(id) {
|
||||
return getMaintenanceRelativeURL(id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show delete confirmation
|
||||
* @param {number} maintenanceID
|
||||
*/
|
||||
deleteDialog(maintenanceID) {
|
||||
this.selectedMaintenanceID = maintenanceID;
|
||||
this.$refs.confirmDelete.show();
|
||||
},
|
||||
|
||||
/** Delete maintenance after showing confirmation dialog */
|
||||
deleteMaintenance() {
|
||||
this.$root.deleteMaintenance(this.selectedMaintenanceID, (res) => {
|
||||
if (res.ok) {
|
||||
|
@ -79,6 +79,22 @@ export function getResBaseURL() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} mqtt wheather or not the regex should take into account the fact that it is an mqtt uri
|
||||
* @returns RegExp The requested regex
|
||||
*/
|
||||
export function hostNameRegexPattern(mqtt = false) {
|
||||
// mqtt, mqtts, ws and wss schemes accepted by mqtt.js (https://github.com/mqttjs/MQTT.js/#connect)
|
||||
const mqttSchemeRegexPattern = "((mqtt|ws)s?:\\/\\/)?";
|
||||
// Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/
|
||||
const ipRegexPattern = `((^\\s*${mqtt ? mqttSchemeRegexPattern : ""}((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))`;
|
||||
// Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
|
||||
const hostNameRegexPattern = `^${mqtt ? mqttSchemeRegexPattern : ""}([a-zA-Z0-9])?(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$`;
|
||||
|
||||
return `${ipRegexPattern}|${hostNameRegexPattern}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag color options
|
||||
* Shared between components
|
||||
|
16
src/util.js
16
src/util.js
@ -315,6 +315,11 @@ function getMonitorRelativeURL(id) {
|
||||
return "/dashboard/" + id;
|
||||
}
|
||||
exports.getMonitorRelativeURL = getMonitorRelativeURL;
|
||||
/**
|
||||
* Get relative path for maintenance
|
||||
* @param id ID of maintenance
|
||||
* @returns Formatted relative path
|
||||
*/
|
||||
function getMaintenanceRelativeURL(id) {
|
||||
return "/maintenance/" + id;
|
||||
}
|
||||
@ -361,6 +366,11 @@ function parseTimeFromTimeObject(obj) {
|
||||
return result;
|
||||
}
|
||||
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
|
||||
/**
|
||||
* Convert ISO date to UTC
|
||||
* @param input Date
|
||||
* @returns ISO Date time
|
||||
*/
|
||||
function isoToUTCDateTime(input) {
|
||||
return dayjs(input).utc().format(exports.SQL_DATETIME_FORMAT);
|
||||
}
|
||||
@ -379,6 +389,12 @@ function utcToLocal(input, format = exports.SQL_DATETIME_FORMAT) {
|
||||
return dayjs.utc(input).local().format(format);
|
||||
}
|
||||
exports.utcToLocal = utcToLocal;
|
||||
/**
|
||||
* Convert local datetime to UTC
|
||||
* @param input Local date
|
||||
* @param format Format to return
|
||||
* @returns Date in requested format
|
||||
*/
|
||||
function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) {
|
||||
return dayjs(input).utc().format(format);
|
||||
}
|
||||
|
17
src/util.ts
17
src/util.ts
@ -352,6 +352,11 @@ export function getMonitorRelativeURL(id: string) {
|
||||
return "/dashboard/" + id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relative path for maintenance
|
||||
* @param id ID of maintenance
|
||||
* @returns Formatted relative path
|
||||
*/
|
||||
export function getMaintenanceRelativeURL(id: string) {
|
||||
return "/maintenance/" + id;
|
||||
}
|
||||
@ -405,7 +410,11 @@ export function parseTimeFromTimeObject(obj : any) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert ISO date to UTC
|
||||
* @param input Date
|
||||
* @returns ISO Date time
|
||||
*/
|
||||
export function isoToUTCDateTime(input : string) {
|
||||
return dayjs(input).utc().format(SQL_DATETIME_FORMAT);
|
||||
}
|
||||
@ -424,6 +433,12 @@ export function utcToLocal(input : string, format = SQL_DATETIME_FORMAT) {
|
||||
return dayjs.utc(input).local().format(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert local datetime to UTC
|
||||
* @param input Local date
|
||||
* @param format Format to return
|
||||
* @returns Date in requested format
|
||||
*/
|
||||
export function localToUTC(input : string, format = SQL_DATETIME_FORMAT) {
|
||||
return dayjs(input).utc().format(format);
|
||||
}
|
||||
|
33
test/cypress/unit/util-frontend.spec.js
Normal file
33
test/cypress/unit/util-frontend.spec.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { hostNameRegexPattern } from "../../../src/util-frontend";
|
||||
|
||||
describe("Test util-frontend.js", () => {
|
||||
|
||||
describe("hostNameRegexPattern()", () => {
|
||||
it('should return a valid regex for non mqtt hostnames', () => {
|
||||
const regex = new RegExp(hostNameRegexPattern(false));
|
||||
|
||||
expect(regex.test("www.test.com")).to.be.true;
|
||||
expect(regex.test("127.0.0.1")).to.be.true;
|
||||
expect(regex.test("192.168.1.156")).to.be.true;
|
||||
|
||||
["mqtt", "mqtts", "ws", "wss"].forEach(schema => {
|
||||
expect(regex.test(`${schema}://www.test.com`)).to.be.false;
|
||||
expect(regex.test(`${schema}://127.0.0.1`)).to.be.false;
|
||||
});
|
||||
});
|
||||
it('should return a valid regex for mqtt hostnames', () => {
|
||||
const hostnameString = hostNameRegexPattern(false);
|
||||
console.log('*********', hostnameString, '***********');
|
||||
const regex = new RegExp(hostNameRegexPattern(true));
|
||||
|
||||
expect(regex.test("www.test.com")).to.be.true;
|
||||
expect(regex.test("127.0.0.1")).to.be.true;
|
||||
expect(regex.test("192.168.1.156")).to.be.true;
|
||||
|
||||
["mqtt", "mqtts", "ws", "wss"].forEach(schema => {
|
||||
expect(regex.test(`${schema}://www.test.com`)).to.be.true;
|
||||
expect(regex.test(`${schema}://127.0.0.1`)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user