mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-23 23:04:04 +00:00
Merge from upstream
This commit is contained in:
commit
912686a299
51 changed files with 2194 additions and 1068 deletions
|
@ -32,6 +32,10 @@ if (! exists) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit updated files
|
||||||
|
* @param {string} version Version to update to
|
||||||
|
*/
|
||||||
function commit(version) {
|
function commit(version) {
|
||||||
let msg = "Update to " + version;
|
let msg = "Update to " + version;
|
||||||
|
|
||||||
|
@ -47,6 +51,10 @@ function commit(version) {
|
||||||
console.log(res.stdout.toString().trim());
|
console.log(res.stdout.toString().trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tag with the specified version
|
||||||
|
* @param {string} version Tag to create
|
||||||
|
*/
|
||||||
function tag(version) {
|
function tag(version) {
|
||||||
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
||||||
console.log(res.stdout.toString().trim());
|
console.log(res.stdout.toString().trim());
|
||||||
|
@ -55,6 +63,11 @@ function tag(version) {
|
||||||
console.log(res.stdout.toString().trim());
|
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) {
|
function tagExists(version) {
|
||||||
if (! version) {
|
if (! version) {
|
||||||
throw new Error("invalid version");
|
throw new Error("invalid version");
|
||||||
|
|
|
@ -25,6 +25,10 @@ if (platform === "linux/amd64") {
|
||||||
const file = fs.createWriteStream("cloudflared.deb");
|
const file = fs.createWriteStream("cloudflared.deb");
|
||||||
get("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-" + arch + ".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) {
|
function get(url) {
|
||||||
http.get(url, function (res) {
|
http.get(url, function (res) {
|
||||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||||
|
|
|
@ -43,6 +43,11 @@ const main = async () => {
|
||||||
console.log("Finished.");
|
console.log("Finished.");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask question of user
|
||||||
|
* @param {string} question Question to ask
|
||||||
|
* @returns {Promise<string>} Users response
|
||||||
|
*/
|
||||||
function question(question) {
|
function question(question) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
rl.question(question, (answer) => {
|
rl.question(question, (answer) => {
|
||||||
|
|
|
@ -53,6 +53,11 @@ const main = async () => {
|
||||||
console.log("Finished.");
|
console.log("Finished.");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask question of user
|
||||||
|
* @param {string} question Question to ask
|
||||||
|
* @returns {Promise<string>} Users response
|
||||||
|
*/
|
||||||
function question(question) {
|
function question(question) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
rl.question(question, (answer) => {
|
rl.question(question, (answer) => {
|
||||||
|
|
|
@ -135,6 +135,11 @@ server.listen({
|
||||||
udp: 5300
|
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) {
|
function type(code) {
|
||||||
for (let name in Packet.TYPE) {
|
for (let name in Packet.TYPE) {
|
||||||
if (Packet.TYPE[name] === code) {
|
if (Packet.TYPE[name] === code) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ class SimpleMqttServer {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Start the MQTT server */
|
||||||
start() {
|
start() {
|
||||||
this.server.listen(this.port, () => {
|
this.server.listen(this.port, () => {
|
||||||
console.log("server started and listening on port ", 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.
|
* Commit updated files
|
||||||
* @param {string} version - The new version number
|
* @param {string} version Version to update to
|
||||||
*
|
|
||||||
* Generated by Trelent
|
|
||||||
*/
|
*/
|
||||||
function commit(version) {
|
function commit(version) {
|
||||||
let msg = "Update to " + 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) {
|
function tag(version) {
|
||||||
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
||||||
console.log(res.stdout.toString().trim());
|
console.log(res.stdout.toString().trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a given version is already tagged in the git repository.
|
* Check if a tag exists for the specified version
|
||||||
* @param {string} version - The version to check for.
|
* @param {string} version Version to check
|
||||||
*
|
* @returns {boolean} Does the tag already exist
|
||||||
* Generated by Trelent
|
|
||||||
*/
|
*/
|
||||||
function tagExists(version) {
|
function tagExists(version) {
|
||||||
if (! version) {
|
if (! version) {
|
||||||
|
|
|
@ -10,6 +10,10 @@ if (!newVersion) {
|
||||||
|
|
||||||
updateWiki(newVersion);
|
updateWiki(newVersion);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the wiki with new version number
|
||||||
|
* @param {string} newVersion Version to update to
|
||||||
|
*/
|
||||||
function updateWiki(newVersion) {
|
function updateWiki(newVersion) {
|
||||||
const wikiDir = "./tmp/wiki";
|
const wikiDir = "./tmp/wiki";
|
||||||
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
|
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
|
||||||
|
@ -39,6 +43,10 @@ function updateWiki(newVersion) {
|
||||||
safeDelete(wikiDir);
|
safeDelete(wikiDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a directory exists and then delete it
|
||||||
|
* @param {string} dir Directory to delete
|
||||||
|
*/
|
||||||
function safeDelete(dir) {
|
function safeDelete(dir) {
|
||||||
if (fs.existsSync(dir)) {
|
if (fs.existsSync(dir)) {
|
||||||
fs.rm(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",
|
"name": "uptime-kuma",
|
||||||
"version": "1.19.4",
|
"version": "1.19.6",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"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-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",
|
"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",
|
"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",
|
"download-dist": "node extra/download-dist.js",
|
||||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||||
"reset-password": "node extra/reset-password.js",
|
"reset-password": "node extra/reset-password.js",
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grpc/grpc-js": "~1.7.3",
|
"@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",
|
"@louislam/sqlite3": "15.1.2",
|
||||||
"args-parser": "~1.3.0",
|
"args-parser": "~1.3.0",
|
||||||
"axios": "~0.27.0",
|
"axios": "~0.27.0",
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
"prom-client": "~13.2.0",
|
"prom-client": "~13.2.0",
|
||||||
"prometheus-api-metrics": "~3.2.1",
|
"prometheus-api-metrics": "~3.2.1",
|
||||||
"protobufjs": "~7.1.1",
|
"protobufjs": "~7.1.1",
|
||||||
"redbean-node": "0.1.4",
|
"redbean-node": "~0.2.0",
|
||||||
"redis": "~4.5.1",
|
"redis": "~4.5.1",
|
||||||
"socket.io": "~4.5.3",
|
"socket.io": "~4.5.3",
|
||||||
"socket.io-client": "~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) {
|
exports.basicAuth = async function (req, res, next) {
|
||||||
const middleware = basicAuth({
|
const middleware = basicAuth({
|
||||||
authorizer: myAuthorizer,
|
authorizer: myAuthorizer,
|
||||||
|
|
|
@ -37,6 +37,10 @@ class CacheableDnsHttpAgent {
|
||||||
this.enable = isEnable;
|
this.enable = isEnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach cacheable to HTTP agent
|
||||||
|
* @param {http.Agent} agent Agent to install
|
||||||
|
*/
|
||||||
static install(agent) {
|
static install(agent) {
|
||||||
this.cacheable.install(agent);
|
this.cacheable.install(agent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ const initBackgroundJobs = function (args) {
|
||||||
return bree;
|
return bree;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Stop all background jobs if running */
|
||||||
const stopBackgroundJobs = function () {
|
const stopBackgroundJobs = function () {
|
||||||
if (bree) {
|
if (bree) {
|
||||||
bree.stop();
|
bree.stop();
|
||||||
|
|
|
@ -112,6 +112,11 @@ class Maintenance extends BeanModel {
|
||||||
return this.toPublicJSON(timezone);
|
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() {
|
getDayOfWeekList() {
|
||||||
log.debug("timeslot", "List: " + this.weekdays);
|
log.debug("timeslot", "List: " + this.weekdays);
|
||||||
return JSON.parse(this.weekdays).sort(function (a, b) {
|
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() {
|
getDayOfMonthList() {
|
||||||
return JSON.parse(this.days_of_month).sort(function (a, b) {
|
return JSON.parse(this.days_of_month).sort(function (a, b) {
|
||||||
return a - b;
|
return a - b;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start date and time for maintenance
|
||||||
|
* @returns {dayjs.Dayjs} Start date and time
|
||||||
|
*/
|
||||||
getStartDateTime() {
|
getStartDateTime() {
|
||||||
let startOfTheDay = dayjs.utc(this.start_date).format("HH:mm");
|
let startOfTheDay = dayjs.utc(this.start_date).format("HH:mm");
|
||||||
log.debug("timeslot", "startOfTheDay: " + startOfTheDay);
|
log.debug("timeslot", "startOfTheDay: " + startOfTheDay);
|
||||||
|
@ -137,6 +150,10 @@ class Maintenance extends BeanModel {
|
||||||
return dayjs.utc(this.start_date).add(startTimeSecond, "second");
|
return dayjs.utc(this.start_date).add(startTimeSecond, "second");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the duraction of maintenance in seconds
|
||||||
|
* @returns {number} Duration of maintenance
|
||||||
|
*/
|
||||||
getDuration() {
|
getDuration() {
|
||||||
let duration = dayjs.utc(this.end_time, "HH:mm").diff(dayjs.utc(this.start_time, "HH:mm"), "second");
|
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
|
// Add 24hours if it is across day
|
||||||
|
@ -146,6 +163,12 @@ class Maintenance extends BeanModel {
|
||||||
return duration;
|
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) {
|
static jsonToBean(bean, obj) {
|
||||||
if (obj.id) {
|
if (obj.id) {
|
||||||
bean.id = obj.id;
|
bean.id = obj.id;
|
||||||
|
|
|
@ -6,6 +6,11 @@ const { UptimeKumaServer } = require("../uptime-kuma-server");
|
||||||
|
|
||||||
class MaintenanceTimeslot extends BeanModel {
|
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() {
|
async toPublicJSON() {
|
||||||
const serverTimezoneOffset = UptimeKumaServer.getInstance().getTimezoneOffset();
|
const serverTimezoneOffset = UptimeKumaServer.getInstance().getTimezoneOffset();
|
||||||
|
|
||||||
|
@ -21,6 +26,10 @@ class MaintenanceTimeslot extends BeanModel {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an object that ready to parse to JSON
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
async toJSON() {
|
async toJSON() {
|
||||||
return await this.toPublicJSON();
|
return await this.toPublicJSON();
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ class Monitor extends BeanModel {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
sendUrl: this.sendUrl,
|
sendUrl: this.sendUrl,
|
||||||
maintenance: await Monitor.isUnderMaintenance(this.id),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.sendUrl) {
|
if (this.sendUrl) {
|
||||||
|
@ -496,14 +495,18 @@ class Monitor extends BeanModel {
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
url: `/containers/${this.docker_container}/json`,
|
url: `/containers/${this.docker_container}/json`,
|
||||||
|
timeout: this.interval * 1000 * 0.8,
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
"User-Agent": "Uptime-Kuma/" + version,
|
"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)
|
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,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dockerHost._dockerType === "socket") {
|
if (dockerHost._dockerType === "socket") {
|
||||||
|
@ -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) {
|
async makeAxiosRequest(options, finalCall = false) {
|
||||||
try {
|
try {
|
||||||
let res;
|
let res;
|
||||||
|
@ -1246,6 +1256,7 @@ class Monitor extends BeanModel {
|
||||||
return maintenance.count !== 0;
|
return maintenance.count !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Make sure monitor interval is between bounds */
|
||||||
validate() {
|
validate() {
|
||||||
if (this.interval > MAX_INTERVAL_SECOND) {
|
if (this.interval > MAX_INTERVAL_SECOND) {
|
||||||
throw new Error(`Interval cannot be more than ${MAX_INTERVAL_SECOND} seconds`);
|
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) {
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
let okMsg = "Sent Successfully.";
|
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 {
|
try {
|
||||||
let config = {
|
let config = {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -18,8 +26,9 @@ class PromoSMS extends NotificationProvider {
|
||||||
};
|
};
|
||||||
let data = {
|
let data = {
|
||||||
"recipients": [ notification.promosmsPhoneNumber ],
|
"recipients": [ notification.promosmsPhoneNumber ],
|
||||||
//Lets remove non ascii char
|
//Trim message to maximum length of 1 SMS or 4 if we allowed long messages
|
||||||
"text": msg.replace(/[^\x00-\x7F]/g, ""),
|
"text": notification.promosmsAllowLongSMS ? cleanMsg.substring(0, 639) : cleanMsg.substring(0, 159),
|
||||||
|
"long-sms": notification.promosmsAllowLongSMS,
|
||||||
"type": Number(notification.promosmsSMSType),
|
"type": Number(notification.promosmsSMSType),
|
||||||
"sender": notification.promosmsSenderName
|
"sender": notification.promosmsSenderName
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Pushover extends NotificationProvider {
|
||||||
let pushoverlink = "https://api.pushover.net/1/messages.json";
|
let pushoverlink = "https://api.pushover.net/1/messages.json";
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
"message": "<b>Message</b>:" + msg,
|
"message": msg,
|
||||||
"user": notification.pushoveruserkey,
|
"user": notification.pushoveruserkey,
|
||||||
"token": notification.pushoverapptoken,
|
"token": notification.pushoverapptoken,
|
||||||
"sound": notification.pushoversounds,
|
"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) {
|
checkStatus(heartbeatJSON, monitorJSON) {
|
||||||
let title = "UptimeKuma Message";
|
let title = "UptimeKuma Message";
|
||||||
if (heartbeatJSON != null && heartbeatJSON["status"] === UP) {
|
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 Teams = require("./notification-providers/teams");
|
||||||
const TechulusPush = require("./notification-providers/techulus-push");
|
const TechulusPush = require("./notification-providers/techulus-push");
|
||||||
const Telegram = require("./notification-providers/telegram");
|
const Telegram = require("./notification-providers/telegram");
|
||||||
|
const Splunk = require("./notification-providers/splunk");
|
||||||
const Webhook = require("./notification-providers/webhook");
|
const Webhook = require("./notification-providers/webhook");
|
||||||
const WeCom = require("./notification-providers/wecom");
|
const WeCom = require("./notification-providers/wecom");
|
||||||
const GoAlert = require("./notification-providers/goalert");
|
const GoAlert = require("./notification-providers/goalert");
|
||||||
|
@ -100,6 +101,7 @@ class Notification {
|
||||||
new Teams(),
|
new Teams(),
|
||||||
new TechulusPush(),
|
new TechulusPush(),
|
||||||
new Telegram(),
|
new Telegram(),
|
||||||
|
new Splunk(),
|
||||||
new Webhook(),
|
new Webhook(),
|
||||||
new WeCom(),
|
new WeCom(),
|
||||||
new GoAlert(),
|
new GoAlert(),
|
||||||
|
|
|
@ -99,6 +99,7 @@ class Prometheus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Remove monitor from prometheus */
|
||||||
remove() {
|
remove() {
|
||||||
try {
|
try {
|
||||||
monitorCertDaysRemaining.remove(this.monitorLabelValues);
|
monitorCertDaysRemaining.remove(this.monitorLabelValues);
|
||||||
|
|
|
@ -6,10 +6,10 @@ class UptimeCacheList {
|
||||||
static list = {};
|
static list = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Get the uptime for a specific period
|
||||||
* @param monitorID
|
* @param {number} monitorID
|
||||||
* @param duration
|
* @param {number} duration
|
||||||
* @return number
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
static getUptime(monitorID, duration) {
|
static getUptime(monitorID, duration) {
|
||||||
if (UptimeCacheList.list[monitorID] && UptimeCacheList.list[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) {
|
static addUptime(monitorID, duration, uptime) {
|
||||||
log.debug("UptimeCacheList", "addUptime: " + monitorID + " " + duration);
|
log.debug("UptimeCacheList", "addUptime: " + monitorID + " " + duration);
|
||||||
if (!UptimeCacheList.list[monitorID]) {
|
if (!UptimeCacheList.list[monitorID]) {
|
||||||
|
@ -28,6 +34,10 @@ class UptimeCacheList {
|
||||||
UptimeCacheList.list[monitorID][duration] = uptime;
|
UptimeCacheList.list[monitorID][duration] = uptime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear cache for specified monitor
|
||||||
|
* @param {number} monitorID
|
||||||
|
*/
|
||||||
static clearCache(monitorID) {
|
static clearCache(monitorID) {
|
||||||
log.debug("UptimeCacheList", "clearCache: " + monitorID);
|
log.debug("UptimeCacheList", "clearCache: " + monitorID);
|
||||||
delete UptimeCacheList.list[monitorID];
|
delete UptimeCacheList.list[monitorID];
|
||||||
|
|
|
@ -86,6 +86,7 @@ class UptimeKumaServer {
|
||||||
this.io = new Server(this.httpServer);
|
this.io = new Server(this.httpServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Initialise app after the database has been set up */
|
||||||
async initAfterDatabaseReady() {
|
async initAfterDatabaseReady() {
|
||||||
await CacheableDnsHttpAgent.update();
|
await CacheableDnsHttpAgent.update();
|
||||||
|
|
||||||
|
@ -98,6 +99,11 @@ class UptimeKumaServer {
|
||||||
this.generateMaintenanceTimeslotsInterval = setInterval(this.generateMaintenanceTimeslots, 60 * 1000);
|
this.generateMaintenanceTimeslotsInterval = setInterval(this.generateMaintenanceTimeslots, 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send list of monitors to client
|
||||||
|
* @param {Socket} socket
|
||||||
|
* @returns {Object} List of monitors
|
||||||
|
*/
|
||||||
async sendMonitorList(socket) {
|
async sendMonitorList(socket) {
|
||||||
let list = await this.getMonitorJSONList(socket.userID);
|
let list = await this.getMonitorJSONList(socket.userID);
|
||||||
this.io.to(socket.userID).emit("monitorList", list);
|
this.io.to(socket.userID).emit("monitorList", list);
|
||||||
|
@ -134,6 +140,11 @@ class UptimeKumaServer {
|
||||||
return await this.sendMaintenanceListByUserID(socket.userID);
|
return await this.sendMaintenanceListByUserID(socket.userID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send list of maintenances to user
|
||||||
|
* @param {number} userID
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
async sendMaintenanceListByUserID(userID) {
|
async sendMaintenanceListByUserID(userID) {
|
||||||
let list = await this.getMaintenanceJSONList(userID);
|
let list = await this.getMaintenanceJSONList(userID);
|
||||||
this.io.to(userID).emit("maintenanceList", list);
|
this.io.to(userID).emit("maintenanceList", list);
|
||||||
|
@ -185,6 +196,11 @@ class UptimeKumaServer {
|
||||||
errorLogStream.end();
|
errorLogStream.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the IP of the client connected to the socket
|
||||||
|
* @param {Socket} socket
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
async getClientIP(socket) {
|
async getClientIP(socket) {
|
||||||
let clientIP = socket.client.conn.remoteAddress;
|
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() {
|
async getTimezone() {
|
||||||
let timezone = await Settings.get("serverTimezone");
|
let timezone = await Settings.get("serverTimezone");
|
||||||
if (timezone) {
|
if (timezone) {
|
||||||
|
@ -214,16 +236,25 @@ class UptimeKumaServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current offset
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
getTimezoneOffset() {
|
getTimezoneOffset() {
|
||||||
return dayjs().format("Z");
|
return dayjs().format("Z");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current server timezone and environment variables
|
||||||
|
* @param {string} timezone
|
||||||
|
*/
|
||||||
async setTimezone(timezone) {
|
async setTimezone(timezone) {
|
||||||
await Settings.set("serverTimezone", timezone, "general");
|
await Settings.set("serverTimezone", timezone, "general");
|
||||||
process.env.TZ = timezone;
|
process.env.TZ = timezone;
|
||||||
dayjs.tz.setDefault(timezone);
|
dayjs.tz.setDefault(timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Load the timeslots for maintenance */
|
||||||
async generateMaintenanceTimeslots() {
|
async generateMaintenanceTimeslots() {
|
||||||
|
|
||||||
let list = await R.find("maintenance_timeslot", " generated_next = 0 AND start_date <= DATETIME('now') ");
|
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() {
|
async stop() {
|
||||||
clearTimeout(this.generateMaintenanceTimeslotsInterval);
|
clearTimeout(this.generateMaintenanceTimeslotsInterval);
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ exports.pingAsync = function (hostname, ipv6 = false) {
|
||||||
ping.promise.probe(hostname, {
|
ping.promise.probe(hostname, {
|
||||||
v6: ipv6,
|
v6: ipv6,
|
||||||
min_reply: 1,
|
min_reply: 1,
|
||||||
timeout: 10,
|
deadline: 10,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
// If ping failed, it will set field to unknown
|
// If ping failed, it will set field to unknown
|
||||||
if (res.alive) {
|
if (res.alive) {
|
||||||
|
@ -137,7 +137,7 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
||||||
const { port, username, password, interval = 20 } = options;
|
const { port, username, password, interval = 20 } = options;
|
||||||
|
|
||||||
// Adds MQTT protocol to the hostname if not already present
|
// 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;
|
hostname = "mqtt://" + hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,10 +147,11 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
||||||
reject(new Error("Timeout"));
|
reject(new Error("Timeout"));
|
||||||
}, interval * 1000 * 0.8);
|
}, interval * 1000 * 0.8);
|
||||||
|
|
||||||
log.debug("mqtt", "MQTT connecting");
|
const mqttUrl = `${hostname}:${port}`;
|
||||||
|
|
||||||
let client = mqtt.connect(hostname, {
|
log.debug("mqtt", `MQTT connecting to ${mqttUrl}`);
|
||||||
port,
|
|
||||||
|
let client = mqtt.connect(mqttUrl, {
|
||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
@ -282,18 +283,23 @@ exports.postgresQuery = function (connectionString, query) {
|
||||||
|
|
||||||
const client = new Client({ connectionString });
|
const client = new Client({ connectionString });
|
||||||
|
|
||||||
client.connect();
|
client.connect((err) => {
|
||||||
|
if (err) {
|
||||||
return client.query(query)
|
|
||||||
.then(res => {
|
|
||||||
resolve(res);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
reject(err);
|
||||||
})
|
client.end();
|
||||||
.finally(() => {
|
} else {
|
||||||
|
// Connected here
|
||||||
|
client.query(query, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(res);
|
||||||
|
}
|
||||||
client.end();
|
client.end();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -91,11 +91,16 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
/** Confirm deletion of docker host */
|
||||||
deleteConfirm() {
|
deleteConfirm() {
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
this.$refs.confirmDelete.show();
|
this.$refs.confirmDelete.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show specified docker host
|
||||||
|
* @param {number} dockerHostID
|
||||||
|
*/
|
||||||
show(dockerHostID) {
|
show(dockerHostID) {
|
||||||
if (dockerHostID) {
|
if (dockerHostID) {
|
||||||
let found = false;
|
let found = false;
|
||||||
|
@ -126,6 +131,7 @@ export default {
|
||||||
this.modal.show();
|
this.modal.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Add docker host */
|
||||||
submit() {
|
submit() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.$root.getSocket().emit("addDockerHost", this.dockerHost, this.id, (res) => {
|
this.$root.getSocket().emit("addDockerHost", this.dockerHost, this.id, (res) => {
|
||||||
|
@ -144,6 +150,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Test the docker host */
|
||||||
test() {
|
test() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.$root.getSocket().emit("testDockerHost", this.dockerHost, (res) => {
|
this.$root.getSocket().emit("testDockerHost", this.dockerHost, (res) => {
|
||||||
|
@ -152,6 +159,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Delete this docker host */
|
||||||
deleteDockerHost() {
|
deleteDockerHost() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.$root.getSocket().emit("deleteDockerHost", this.id, (res) => {
|
this.$root.getSocket().emit("deleteDockerHost", this.id, (res) => {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<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="d-flex">
|
||||||
<div class="col-8 pe-1">
|
<div class="col-8 pe-1">
|
||||||
<vue-multiselect
|
<vue-multiselect
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { DOWN, MAINTENANCE, PENDING, UP } from "../util.ts";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
/** Monitor this represents */
|
/** Monitor this represents */
|
||||||
|
@ -24,7 +26,6 @@ export default {
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
uptime() {
|
uptime() {
|
||||||
|
|
||||||
if (this.type === "maintenance") {
|
if (this.type === "maintenance") {
|
||||||
return this.$t("statusMaintenance");
|
return this.$t("statusMaintenance");
|
||||||
}
|
}
|
||||||
|
@ -39,19 +40,19 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
color() {
|
color() {
|
||||||
if (this.type === "maintenance" || this.monitor.maintenance) {
|
if (this.lastHeartBeat.status === MAINTENANCE) {
|
||||||
return "maintenance";
|
return "maintenance";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lastHeartBeat.status === 0) {
|
if (this.lastHeartBeat.status === DOWN) {
|
||||||
return "danger";
|
return "danger";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lastHeartBeat.status === 1) {
|
if (this.lastHeartBeat.status === UP) {
|
||||||
return "primary";
|
return "primary";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lastHeartBeat.status === 2) {
|
if (this.lastHeartBeat.status === PENDING) {
|
||||||
return "warning";
|
return "warning";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
<label for="promosms-sender-name" class="form-label">{{ $t("promosmsSMSSender") }}</label>
|
<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">
|
<input id="promosms-sender-name" v-model="$parent.notification.promosmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||||
</div>
|
</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>
|
</template>
|
||||||
|
|
||||||
<script>
|
<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,
|
HiddenInput,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* Get the URL for telegram updates
|
||||||
|
* @param {string} [mode=masked] Should the token be masked?
|
||||||
|
* @returns {string} formatted URL
|
||||||
|
*/
|
||||||
telegramGetUpdatesURL(mode = "masked") {
|
telegramGetUpdatesURL(mode = "masked") {
|
||||||
let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`;
|
let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`;
|
||||||
|
|
||||||
|
@ -55,6 +60,8 @@ export default {
|
||||||
|
|
||||||
return `https://api.telegram.org/bot${token}/getUpdates`;
|
return `https://api.telegram.org/bot${token}/getUpdates`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Get the telegram chat ID */
|
||||||
async autoGetTelegramChatID() {
|
async autoGetTelegramChatID() {
|
||||||
try {
|
try {
|
||||||
let res = await axios.get(this.telegramGetUpdatesURL("withToken"));
|
let res = await axios.get(this.telegramGetUpdatesURL("withToken"));
|
||||||
|
|
|
@ -44,6 +44,7 @@ import Webhook from "./Webhook.vue";
|
||||||
import WeCom from "./WeCom.vue";
|
import WeCom from "./WeCom.vue";
|
||||||
import GoAlert from "./GoAlert.vue";
|
import GoAlert from "./GoAlert.vue";
|
||||||
import ZohoCliq from "./ZohoCliq.vue";
|
import ZohoCliq from "./ZohoCliq.vue";
|
||||||
|
import Splunk from "./Splunk.vue";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage all notification form.
|
* Manage all notification form.
|
||||||
|
@ -92,6 +93,7 @@ const NotificationFormList = {
|
||||||
"stackfield": Stackfield,
|
"stackfield": Stackfield,
|
||||||
"teams": Teams,
|
"teams": Teams,
|
||||||
"telegram": Telegram,
|
"telegram": Telegram,
|
||||||
|
"Splunk": Splunk,
|
||||||
"webhook": Webhook,
|
"webhook": Webhook,
|
||||||
"WeCom": WeCom,
|
"WeCom": WeCom,
|
||||||
"GoAlert": GoAlert,
|
"GoAlert": GoAlert,
|
||||||
|
|
|
@ -191,6 +191,7 @@ export default {
|
||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Show confirmation dialog for disable auth */
|
||||||
confirmDisableAuth() {
|
confirmDisableAuth() {
|
||||||
this.$refs.confirmDisableAuth.show();
|
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";
|
import en from "./lang/en.json";
|
||||||
|
|
||||||
const languageList = {
|
const languageList = {
|
||||||
|
"ar-SY": "العربية",
|
||||||
"cs-CZ": "Čeština",
|
"cs-CZ": "Čeština",
|
||||||
"zh-HK": "繁體中文 (香港)",
|
"zh-HK": "繁體中文 (香港)",
|
||||||
"bg-BG": "Български",
|
"bg-BG": "Български",
|
||||||
|
@ -48,7 +49,7 @@ for (let lang in languageList) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const rtlLangs = [ "fa" ];
|
const rtlLangs = [ "fa", "ar-SY" ];
|
||||||
|
|
||||||
export const currentLocale = () => localStorage.locale
|
export const currentLocale = () => localStorage.locale
|
||||||
|| languageList[navigator.language] && navigator.language
|
|| languageList[navigator.language] && navigator.language
|
||||||
|
|
|
@ -44,6 +44,7 @@ import {
|
||||||
faWrench,
|
faWrench,
|
||||||
faHeartbeat,
|
faHeartbeat,
|
||||||
faFilter,
|
faFilter,
|
||||||
|
faInfoCircle,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
|
@ -88,6 +89,7 @@ library.add(
|
||||||
faWrench,
|
faWrench,
|
||||||
faHeartbeat,
|
faHeartbeat,
|
||||||
faFilter,
|
faFilter,
|
||||||
|
faInfoCircle,
|
||||||
);
|
);
|
||||||
|
|
||||||
export { FontAwesomeIcon };
|
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",
|
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Á.",
|
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í.",
|
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ěď.",
|
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í",
|
passwordNotMatchMsg: "Hesla se neshodují",
|
||||||
notificationDescription: "Pro zajištění funkčnosti oznámení je nutné jej přiřadit dohledu.",
|
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.",
|
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?",
|
deleteMonitorMsg: "Opravdu chcete odstranit tento dohled?",
|
||||||
|
deleteMaintenanceMsg: "Opravdu chcete odstranit tuto údržbu?",
|
||||||
deleteNotificationMsg: "Opravdu chcete odstranit toto oznámení pro všechny dohledy?",
|
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.",
|
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.",
|
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ý",
|
Down: "Nedostupný",
|
||||||
Pending: "Čekám",
|
Pending: "Čekám",
|
||||||
Unknown: "Neznámý",
|
Unknown: "Neznámý",
|
||||||
Pause: "Pozastaveno",
|
Pause: "Pozastavit",
|
||||||
Name: "Název",
|
Name: "Název",
|
||||||
Status: "Stav",
|
Status: "Stav",
|
||||||
DateTime: "Časové razítko",
|
DateTime: "Časové razítko",
|
||||||
|
@ -59,6 +74,7 @@ export default {
|
||||||
Current: "Aktuální",
|
Current: "Aktuální",
|
||||||
Uptime: "Doba provozu",
|
Uptime: "Doba provozu",
|
||||||
"Cert Exp.": "Platnost certifikátu",
|
"Cert Exp.": "Platnost certifikátu",
|
||||||
|
Monitor: "Dohled | Dohledy",
|
||||||
day: "den | dny/í",
|
day: "den | dny/í",
|
||||||
"-day": "-dní",
|
"-day": "-dní",
|
||||||
hour: "hodina",
|
hour: "hodina",
|
||||||
|
@ -175,6 +191,7 @@ export default {
|
||||||
Indigo: "Indigo",
|
Indigo: "Indigo",
|
||||||
Purple: "Purpurová",
|
Purple: "Purpurová",
|
||||||
Pink: "Růžová",
|
Pink: "Růžová",
|
||||||
|
Custom: "Vlastní",
|
||||||
"Search...": "Hledat…",
|
"Search...": "Hledat…",
|
||||||
"Avg. Ping": "Průměr Ping",
|
"Avg. Ping": "Průměr Ping",
|
||||||
"Avg. Response": "Průměr Odpověď",
|
"Avg. Response": "Průměr Odpověď",
|
||||||
|
@ -194,6 +211,7 @@ export default {
|
||||||
here: "sem",
|
here: "sem",
|
||||||
Required: "Vyžadováno",
|
Required: "Vyžadováno",
|
||||||
telegram: "Telegram",
|
telegram: "Telegram",
|
||||||
|
"ZohoCliq": "ZohoCliq",
|
||||||
"Bot Token": "Token robota",
|
"Bot Token": "Token robota",
|
||||||
wayToGetTelegramToken: "Token můžete získat od {0}.",
|
wayToGetTelegramToken: "Token můžete získat od {0}.",
|
||||||
"Chat ID": "ID chatu",
|
"Chat ID": "ID chatu",
|
||||||
|
@ -206,6 +224,8 @@ export default {
|
||||||
"Content Type": "Typ obsahu",
|
"Content Type": "Typ obsahu",
|
||||||
webhookJsonDesc: "{0} je vhodný pro všechny moderní servery HTTP, jako je Express.js",
|
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}",
|
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)",
|
smtp: "E-mail (SMTP)",
|
||||||
secureOptionNone: "Žádné / STARTTLS (25, 587)",
|
secureOptionNone: "Žádné / STARTTLS (25, 587)",
|
||||||
secureOptionTLS: "TLS (465)",
|
secureOptionTLS: "TLS (465)",
|
||||||
|
@ -223,7 +243,8 @@ export default {
|
||||||
"Hello @everyone is...": "Dobrý den, {'@'}všichni jsou…",
|
"Hello @everyone is...": "Dobrý den, {'@'}všichni jsou…",
|
||||||
teams: "Microsoft Teams",
|
teams: "Microsoft Teams",
|
||||||
"Webhook URL": "URL adresa webhooku",
|
"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",
|
signal: "Signal",
|
||||||
Number: "Číslo",
|
Number: "Číslo",
|
||||||
Recipients: "Příjemci",
|
Recipients: "Příjemci",
|
||||||
|
@ -253,6 +274,10 @@ export default {
|
||||||
apprise: "Apprise (podpora více než 50 oznamovacích služeb)",
|
apprise: "Apprise (podpora více než 50 oznamovacích služeb)",
|
||||||
GoogleChat: "Google Chat (pouze Google Workspace)",
|
GoogleChat: "Google Chat (pouze Google Workspace)",
|
||||||
pushbullet: "Pushbullet",
|
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",
|
line: "Line Messenger",
|
||||||
mattermost: "Mattermost",
|
mattermost: "Mattermost",
|
||||||
"User Key": "Klíč uživatele",
|
"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).",
|
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)",
|
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",
|
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",
|
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||||
matrixHomeserverURL: "URL adresa domácího serveru (s http(s):// a volitelně portem)",
|
matrixHomeserverURL: "URL adresa domácího serveru (s http(s):// a volitelně portem)",
|
||||||
"Internal Room Id": "ID interní místnosti",
|
"Internal Room Id": "ID interní místnosti",
|
||||||
|
@ -365,6 +391,16 @@ export default {
|
||||||
serwersmsAPIPassword: "API heslo",
|
serwersmsAPIPassword: "API heslo",
|
||||||
serwersmsPhoneNumber: "Telefonní číslo",
|
serwersmsPhoneNumber: "Telefonní číslo",
|
||||||
serwersmsSenderName: "Odesílatel SMS (registrováno prostřednictvím zákaznického portálu)",
|
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",
|
"stackfield": "Stackfield",
|
||||||
Customize: "Přizpůsobit",
|
Customize: "Přizpůsobit",
|
||||||
"Custom Footer": "Vlastní patička",
|
"Custom Footer": "Vlastní patička",
|
||||||
|
@ -588,11 +624,11 @@ export default {
|
||||||
"SMSManager API Docs": "SMSManager API Docs ",
|
"SMSManager API Docs": "SMSManager API Docs ",
|
||||||
"Gateway Type": "Gateway Typ",
|
"Gateway Type": "Gateway Typ",
|
||||||
SMSManager: "SMSManager",
|
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",
|
"or": "nebo",
|
||||||
recurringInterval: "Interval",
|
recurringInterval: "Interval",
|
||||||
"Recurring": "Opakující se",
|
"Recurring": "Opakující se",
|
||||||
strategyManual: "Aktivní/Neaktivní Ručně",
|
strategyManual: "Ruční spuštění/vypnutí",
|
||||||
warningTimezone: "Používá se časové pásmo serveru",
|
warningTimezone: "Používá se časové pásmo serveru",
|
||||||
weekdayShortMon: "Po",
|
weekdayShortMon: "Po",
|
||||||
weekdayShortTue: "Út",
|
weekdayShortTue: "Út",
|
||||||
|
@ -608,8 +644,8 @@ export default {
|
||||||
lastDay2: "2. poslední den v měsíci",
|
lastDay2: "2. poslední den v měsíci",
|
||||||
lastDay3: "3. poslední den v měsíci",
|
lastDay3: "3. poslední den v měsíci",
|
||||||
lastDay4: "4. poslední den v měsíci",
|
lastDay4: "4. poslední den v měsíci",
|
||||||
"No Maintenance": "Žádna údržba",
|
"No Maintenance": "Žádná údržba",
|
||||||
pauseMaintenanceMsg: "Jsi si jistý, že chceš pozastavit údržbu?",
|
pauseMaintenanceMsg: "Opravdu chcete pozastavit údržbu?",
|
||||||
"maintenanceStatus-under-maintenance": "Údržba",
|
"maintenanceStatus-under-maintenance": "Údržba",
|
||||||
"maintenanceStatus-inactive": "Neaktivní",
|
"maintenanceStatus-inactive": "Neaktivní",
|
||||||
"maintenanceStatus-scheduled": "Naplánováno",
|
"maintenanceStatus-scheduled": "Naplánováno",
|
||||||
|
@ -622,5 +658,27 @@ export default {
|
||||||
"Enable DNS Cache": "Povolit DNS Cache",
|
"Enable DNS Cache": "Povolit DNS Cache",
|
||||||
"Enable": "Povolit",
|
"Enable": "Povolit",
|
||||||
"Disable": "Zakázat",
|
"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...",
|
"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 name already exist.": "Tag with this name already exists.",
|
||||||
"Tag with this value already exist.": "Tag with this value already exists.",
|
"Tag with this value already exist.": "Tag with this value already exists.",
|
||||||
color: "color",
|
color: "Color",
|
||||||
"value (optional)": "value (optional)",
|
"value (optional)": "value (optional)",
|
||||||
Gray: "Gray",
|
Gray: "Gray",
|
||||||
Red: "Red",
|
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).",
|
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)",
|
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",
|
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",
|
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||||
matrixHomeserverURL: "Homeserver URL (with http(s):// and optionally port)",
|
matrixHomeserverURL: "Homeserver URL (with http(s):// and optionally port)",
|
||||||
"Internal Room Id": "Internal Room ID",
|
"Internal Room Id": "Internal Room ID",
|
||||||
|
|
|
@ -209,7 +209,7 @@ export default {
|
||||||
here: "ici",
|
here: "ici",
|
||||||
Required: "Requis",
|
Required: "Requis",
|
||||||
telegram: "Telegram",
|
telegram: "Telegram",
|
||||||
"ZohoCliq": "ZohoCliq",
|
ZohoCliq: "ZohoCliq",
|
||||||
"Bot Token": "Jeton du robot",
|
"Bot Token": "Jeton du robot",
|
||||||
wayToGetTelegramToken: "Vous pouvez obtenir un token depuis {0}.",
|
wayToGetTelegramToken: "Vous pouvez obtenir un token depuis {0}.",
|
||||||
"Chat ID": "Chat ID",
|
"Chat ID": "Chat ID",
|
||||||
|
@ -308,7 +308,7 @@ export default {
|
||||||
lineDevConsoleTo: "Console développeurs Line - {0}",
|
lineDevConsoleTo: "Console développeurs Line - {0}",
|
||||||
"Basic Settings": "Paramètres de base",
|
"Basic Settings": "Paramètres de base",
|
||||||
"User ID": "Identifiant utilisateur",
|
"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.",
|
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",
|
"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.",
|
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 Service Name": "Nom du service proto",
|
||||||
"Proto Method": "Méthode Proto",
|
"Proto Method": "Méthode Proto",
|
||||||
"Proto Content": "Contenu proto",
|
"Proto Content": "Contenu proto",
|
||||||
"Economy": "Économique",
|
Economy: "Économique",
|
||||||
"Lowcost": "Faible coût",
|
Lowcost: "Faible coût",
|
||||||
"high": "Haute",
|
high: "Haute",
|
||||||
"General Monitor Type": "Type de sonde générale",
|
"General Monitor Type": "Type de sonde générale",
|
||||||
"Passive Monitor Type": "Type de sonde passive",
|
"Passive Monitor Type": "Type de sonde passive",
|
||||||
"Specific Monitor Type": "Type de sonde spécifique",
|
"Specific Monitor Type": "Type de sonde spécifique",
|
||||||
dataRetentionTimeError: "La durée de conservation doit être supérieure ou égale à 0",
|
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.",
|
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",
|
promosmsTypeSpeed: "SMS SPEED - wysyłka priorytetowa, ma wszystkie zalety SMS FULL",
|
||||||
promosmsPhoneNumber: "Numer odbiorcy",
|
promosmsPhoneNumber: "Numer odbiorcy",
|
||||||
promosmsSMSSender: "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)",
|
promosmsSMSSender: "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)",
|
||||||
|
promosmsAllowLongSMS: "Zezwól na długie SMSy",
|
||||||
"Primary Base URL": "Główny URL",
|
"Primary Base URL": "Główny URL",
|
||||||
"Push URL": "Push URL",
|
"Push URL": "Push URL",
|
||||||
needPushEvery: "Powinieneś wywoływać ten URL co {0} sekund",
|
needPushEvery: "Powinieneś wywoływać ten URL co {0} sekund",
|
||||||
|
|
|
@ -63,6 +63,12 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</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'">
|
<li v-if="$root.loggedIn && $root.socket.token !== 'autoLogin'">
|
||||||
<button class="dropdown-item" @click="$root.logout">
|
<button class="dropdown-item" @click="$root.logout">
|
||||||
<font-awesome-icon icon="sign-out-alt" />
|
<font-awesome-icon icon="sign-out-alt" />
|
||||||
|
|
|
@ -12,6 +12,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* Convert value to UTC
|
||||||
|
* @param {string | number | Date | dayjs.Dayjs} value
|
||||||
|
* @returns {dayjs.Dayjs}
|
||||||
|
*/
|
||||||
toUTC(value) {
|
toUTC(value) {
|
||||||
return dayjs.tz(value, this.timezone).utc().format();
|
return dayjs.tz(value, this.timezone).utc().format();
|
||||||
},
|
},
|
||||||
|
@ -34,6 +39,11 @@ export default {
|
||||||
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss");
|
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) {
|
datetimeMaintenance(value) {
|
||||||
const inputDate = new Date(value);
|
const inputDate = new Date(value);
|
||||||
const now = new Date(Date.now());
|
const now = new Date(Date.now());
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useToast } from "vue-toastification";
|
||||||
import jwtDecode from "jwt-decode";
|
import jwtDecode from "jwt-decode";
|
||||||
import Favico from "favico.js";
|
import Favico from "favico.js";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { DOWN, MAINTENANCE, PENDING, UP } from "../util.ts";
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
let socket;
|
let socket;
|
||||||
|
@ -454,6 +455,10 @@ export default {
|
||||||
socket.emit("getMonitorList", callback);
|
socket.emit("getMonitorList", callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of maintenances
|
||||||
|
* @param {socketCB} callback
|
||||||
|
*/
|
||||||
getMaintenanceList(callback) {
|
getMaintenanceList(callback) {
|
||||||
if (! callback) {
|
if (! callback) {
|
||||||
callback = () => { };
|
callback = () => { };
|
||||||
|
@ -470,22 +475,49 @@ export default {
|
||||||
socket.emit("add", monitor, callback);
|
socket.emit("add", monitor, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a maintenace
|
||||||
|
* @param {Object} maintenance
|
||||||
|
* @param {socketCB} callback
|
||||||
|
*/
|
||||||
addMaintenance(maintenance, callback) {
|
addMaintenance(maintenance, callback) {
|
||||||
socket.emit("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) {
|
addMonitorMaintenance(maintenanceID, monitors, callback) {
|
||||||
socket.emit("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) {
|
addMaintenanceStatusPage(maintenanceID, statusPages, callback) {
|
||||||
socket.emit("addMaintenanceStatusPage", maintenanceID, statusPages, callback);
|
socket.emit("addMaintenanceStatusPage", maintenanceID, statusPages, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get monitors affected by maintenance
|
||||||
|
* @param {number} maintenanceID
|
||||||
|
* @param {socketCB} callback
|
||||||
|
*/
|
||||||
getMonitorMaintenance(maintenanceID, callback) {
|
getMonitorMaintenance(maintenanceID, callback) {
|
||||||
socket.emit("getMonitorMaintenance", maintenanceID, callback);
|
socket.emit("getMonitorMaintenance", maintenanceID, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get status pages where maintenance is shown
|
||||||
|
* @param {number} maintenanceID
|
||||||
|
* @param {socketCB} callback
|
||||||
|
*/
|
||||||
getMaintenanceStatusPage(maintenanceID, callback) {
|
getMaintenanceStatusPage(maintenanceID, callback) {
|
||||||
socket.emit("getMaintenanceStatusPage", maintenanceID, callback);
|
socket.emit("getMaintenanceStatusPage", maintenanceID, callback);
|
||||||
},
|
},
|
||||||
|
@ -499,6 +531,11 @@ export default {
|
||||||
socket.emit("deleteMonitor", monitorID, callback);
|
socket.emit("deleteMonitor", monitorID, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete specified maintenance
|
||||||
|
* @param {number} maintenanceID
|
||||||
|
* @param {socketCB} callback
|
||||||
|
*/
|
||||||
deleteMaintenance(maintenanceID, callback) {
|
deleteMaintenance(maintenanceID, callback) {
|
||||||
socket.emit("deleteMaintenance", maintenanceID, callback);
|
socket.emit("deleteMaintenance", maintenanceID, callback);
|
||||||
},
|
},
|
||||||
|
@ -590,28 +627,28 @@ export default {
|
||||||
for (let monitorID in this.lastHeartbeatList) {
|
for (let monitorID in this.lastHeartbeatList) {
|
||||||
let lastHeartBeat = this.lastHeartbeatList[monitorID];
|
let lastHeartBeat = this.lastHeartbeatList[monitorID];
|
||||||
|
|
||||||
if (this.monitorList[monitorID] && this.monitorList[monitorID].maintenance) {
|
if (! lastHeartBeat) {
|
||||||
result[monitorID] = {
|
|
||||||
text: this.$t("statusMaintenance"),
|
|
||||||
color: "maintenance",
|
|
||||||
};
|
|
||||||
} else if (! lastHeartBeat) {
|
|
||||||
result[monitorID] = unknown;
|
result[monitorID] = unknown;
|
||||||
} else if (lastHeartBeat.status === 1) {
|
} else if (lastHeartBeat.status === UP) {
|
||||||
result[monitorID] = {
|
result[monitorID] = {
|
||||||
text: this.$t("Up"),
|
text: this.$t("Up"),
|
||||||
color: "primary",
|
color: "primary",
|
||||||
};
|
};
|
||||||
} else if (lastHeartBeat.status === 0) {
|
} else if (lastHeartBeat.status === DOWN) {
|
||||||
result[monitorID] = {
|
result[monitorID] = {
|
||||||
text: this.$t("Down"),
|
text: this.$t("Down"),
|
||||||
color: "danger",
|
color: "danger",
|
||||||
};
|
};
|
||||||
} else if (lastHeartBeat.status === 2) {
|
} else if (lastHeartBeat.status === PENDING) {
|
||||||
result[monitorID] = {
|
result[monitorID] = {
|
||||||
text: this.$t("Pending"),
|
text: this.$t("Pending"),
|
||||||
color: "warning",
|
color: "warning",
|
||||||
};
|
};
|
||||||
|
} else if (lastHeartBeat.status === MAINTENANCE) {
|
||||||
|
result[monitorID] = {
|
||||||
|
text: this.$t("statusMaintenance"),
|
||||||
|
color: "maintenance",
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
result[monitorID] = unknown;
|
result[monitorID] = unknown;
|
||||||
}
|
}
|
||||||
|
@ -633,17 +670,17 @@ export default {
|
||||||
let beat = this.$root.lastHeartbeatList[monitorID];
|
let beat = this.$root.lastHeartbeatList[monitorID];
|
||||||
let monitor = this.$root.monitorList[monitorID];
|
let monitor = this.$root.monitorList[monitorID];
|
||||||
|
|
||||||
if (monitor && monitor.maintenance) {
|
if (monitor && ! monitor.active) {
|
||||||
result.maintenance++;
|
|
||||||
} else if (monitor && ! monitor.active) {
|
|
||||||
result.pause++;
|
result.pause++;
|
||||||
} else if (beat) {
|
} else if (beat) {
|
||||||
if (beat.status === 1) {
|
if (beat.status === UP) {
|
||||||
result.up++;
|
result.up++;
|
||||||
} else if (beat.status === 0) {
|
} else if (beat.status === DOWN) {
|
||||||
result.down++;
|
result.down++;
|
||||||
} else if (beat.status === 2) {
|
} else if (beat.status === PENDING) {
|
||||||
result.up++;
|
result.up++;
|
||||||
|
} else if (beat.status === MAINTENANCE) {
|
||||||
|
result.maintenance++;
|
||||||
} else {
|
} else {
|
||||||
result.unknown++;
|
result.unknown++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -356,6 +356,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/** Initialise page */
|
||||||
init() {
|
init() {
|
||||||
this.affectedMonitors = [];
|
this.affectedMonitors = [];
|
||||||
this.selectedStatusPages = [];
|
this.selectedStatusPages = [];
|
||||||
|
@ -414,6 +415,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Create new maintenance */
|
||||||
async submit() {
|
async submit() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
|
@ -458,6 +460,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add monitor to maintenance
|
||||||
|
* @param {number} maintenanceID
|
||||||
|
* @param {socketCB} callback
|
||||||
|
*/
|
||||||
async addMonitorMaintenance(maintenanceID, callback) {
|
async addMonitorMaintenance(maintenanceID, callback) {
|
||||||
await this.$root.addMonitorMaintenance(maintenanceID, this.affectedMonitors, async (res) => {
|
await this.$root.addMonitorMaintenance(maintenanceID, this.affectedMonitors, async (res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
@ -470,6 +477,11 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add status page to maintenance
|
||||||
|
* @param {number} maintenanceID
|
||||||
|
* @param {socketCB} callback
|
||||||
|
*/
|
||||||
async addMaintenanceStatusPage(maintenanceID, callback) {
|
async addMaintenanceStatusPage(maintenanceID, callback) {
|
||||||
await this.$root.addMaintenanceStatusPage(maintenanceID, (this.showOnAllPages) ? this.selectedStatusPagesOptions : this.selectedStatusPages, async (res) => {
|
await this.$root.addMaintenanceStatusPage(maintenanceID, (this.showOnAllPages) ? this.selectedStatusPagesOptions : this.selectedStatusPages, async (res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius only -->
|
<!-- 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">
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
<!-- Port -->
|
<!-- Port -->
|
||||||
|
@ -600,6 +600,7 @@ import DockerHostDialog from "../components/DockerHostDialog.vue";
|
||||||
import ProxyDialog from "../components/ProxyDialog.vue";
|
import ProxyDialog from "../components/ProxyDialog.vue";
|
||||||
import TagsManager from "../components/TagsManager.vue";
|
import TagsManager from "../components/TagsManager.vue";
|
||||||
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } from "../util.ts";
|
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } from "../util.ts";
|
||||||
|
import { hostNameRegexPattern } from "../util-frontend";
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
|
@ -624,11 +625,8 @@ export default {
|
||||||
},
|
},
|
||||||
acceptedStatusCodeOptions: [],
|
acceptedStatusCodeOptions: [],
|
||||||
dnsresolvetypeOptions: [],
|
dnsresolvetypeOptions: [],
|
||||||
|
ipOrHostnameRegexPattern: hostNameRegexPattern(),
|
||||||
// Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/
|
mqttIpOrHostnameRegexPattern: hostNameRegexPattern(true)
|
||||||
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_])$"
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ export default {
|
||||||
this.init();
|
this.init();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/** Initialise page */
|
||||||
init() {
|
init() {
|
||||||
this.$root.getSocket().emit("getMonitorMaintenance", this.$route.params.id, (res) => {
|
this.$root.getSocket().emit("getMonitorMaintenance", this.$route.params.id, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
@ -83,10 +84,12 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Confirm deletion */
|
||||||
deleteDialog() {
|
deleteDialog() {
|
||||||
this.$refs.confirmDelete.show();
|
this.$refs.confirmDelete.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Delete maintenance after showing confirmation */
|
||||||
deleteMaintenance() {
|
deleteMaintenance() {
|
||||||
this.$root.deleteMaintenance(this.maintenance.id, (res) => {
|
this.$root.deleteMaintenance(this.maintenance.id, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|
|
@ -133,15 +133,25 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get maintenance URL
|
||||||
|
* @param {number} id
|
||||||
|
* @returns {string} Relative URL
|
||||||
|
*/
|
||||||
maintenanceURL(id) {
|
maintenanceURL(id) {
|
||||||
return getMaintenanceRelativeURL(id);
|
return getMaintenanceRelativeURL(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show delete confirmation
|
||||||
|
* @param {number} maintenanceID
|
||||||
|
*/
|
||||||
deleteDialog(maintenanceID) {
|
deleteDialog(maintenanceID) {
|
||||||
this.selectedMaintenanceID = maintenanceID;
|
this.selectedMaintenanceID = maintenanceID;
|
||||||
this.$refs.confirmDelete.show();
|
this.$refs.confirmDelete.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Delete maintenance after showing confirmation dialog */
|
||||||
deleteMaintenance() {
|
deleteMaintenance() {
|
||||||
this.$root.deleteMaintenance(this.selectedMaintenanceID, (res) => {
|
this.$root.deleteMaintenance(this.selectedMaintenanceID, (res) => {
|
||||||
if (res.ok) {
|
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
|
* Get the tag color options
|
||||||
* Shared between components
|
* Shared between components
|
||||||
|
|
16
src/util.js
16
src/util.js
|
@ -315,6 +315,11 @@ function getMonitorRelativeURL(id) {
|
||||||
return "/dashboard/" + id;
|
return "/dashboard/" + id;
|
||||||
}
|
}
|
||||||
exports.getMonitorRelativeURL = getMonitorRelativeURL;
|
exports.getMonitorRelativeURL = getMonitorRelativeURL;
|
||||||
|
/**
|
||||||
|
* Get relative path for maintenance
|
||||||
|
* @param id ID of maintenance
|
||||||
|
* @returns Formatted relative path
|
||||||
|
*/
|
||||||
function getMaintenanceRelativeURL(id) {
|
function getMaintenanceRelativeURL(id) {
|
||||||
return "/maintenance/" + id;
|
return "/maintenance/" + id;
|
||||||
}
|
}
|
||||||
|
@ -361,6 +366,11 @@ function parseTimeFromTimeObject(obj) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
|
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
|
||||||
|
/**
|
||||||
|
* Convert ISO date to UTC
|
||||||
|
* @param input Date
|
||||||
|
* @returns ISO Date time
|
||||||
|
*/
|
||||||
function isoToUTCDateTime(input) {
|
function isoToUTCDateTime(input) {
|
||||||
return dayjs(input).utc().format(exports.SQL_DATETIME_FORMAT);
|
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);
|
return dayjs.utc(input).local().format(format);
|
||||||
}
|
}
|
||||||
exports.utcToLocal = utcToLocal;
|
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) {
|
function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) {
|
||||||
return dayjs(input).utc().format(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;
|
return "/dashboard/" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get relative path for maintenance
|
||||||
|
* @param id ID of maintenance
|
||||||
|
* @returns Formatted relative path
|
||||||
|
*/
|
||||||
export function getMaintenanceRelativeURL(id: string) {
|
export function getMaintenanceRelativeURL(id: string) {
|
||||||
return "/maintenance/" + id;
|
return "/maintenance/" + id;
|
||||||
}
|
}
|
||||||
|
@ -405,7 +410,11 @@ export function parseTimeFromTimeObject(obj : any) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert ISO date to UTC
|
||||||
|
* @param input Date
|
||||||
|
* @returns ISO Date time
|
||||||
|
*/
|
||||||
export function isoToUTCDateTime(input : string) {
|
export function isoToUTCDateTime(input : string) {
|
||||||
return dayjs(input).utc().format(SQL_DATETIME_FORMAT);
|
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);
|
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) {
|
export function localToUTC(input : string, format = SQL_DATETIME_FORMAT) {
|
||||||
return dayjs(input).utc().format(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 a new issue