mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-07-21 00:24:04 +02:00
Merge branch 'master' into master
This commit is contained in:
commit
d0b7528761
19 changed files with 182 additions and 46 deletions
|
@ -3,6 +3,7 @@ console.log("== Uptime Kuma Reset Password Tool ==");
|
||||||
const Database = require("../server/database");
|
const Database = require("../server/database");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const readline = require("readline");
|
const readline = require("readline");
|
||||||
|
const { passwordStrength } = require("check-password-strength");
|
||||||
const { initJWTSecret } = require("../server/util-server");
|
const { initJWTSecret } = require("../server/util-server");
|
||||||
const User = require("../server/model/user");
|
const User = require("../server/model/user");
|
||||||
const { io } = require("socket.io-client");
|
const { io } = require("socket.io-client");
|
||||||
|
@ -42,8 +43,15 @@ const main = async () => {
|
||||||
console.log("Using password from argument");
|
console.log("Using password from argument");
|
||||||
console.warn("\x1b[31m%s\x1b[0m", "Warning: the password might be stored, in plain text, in your shell's history");
|
console.warn("\x1b[31m%s\x1b[0m", "Warning: the password might be stored, in plain text, in your shell's history");
|
||||||
password = confirmPassword = args["new-password"] + "";
|
password = confirmPassword = args["new-password"] + "";
|
||||||
|
if (passwordStrength(password).value === "Too weak") {
|
||||||
|
throw new Error("Password is too weak, please use a stronger password.");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
password = await question("New Password: ");
|
password = await question("New Password: ");
|
||||||
|
if (passwordStrength(password).value === "Too weak") {
|
||||||
|
console.log("Password is too weak, please try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
confirmPassword = await question("Confirm New Password: ");
|
confirmPassword = await question("Confirm New Password: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -24,6 +24,7 @@
|
||||||
"command-exists": "~1.2.9",
|
"command-exists": "~1.2.9",
|
||||||
"compare-versions": "~3.6.0",
|
"compare-versions": "~3.6.0",
|
||||||
"compression": "~1.7.4",
|
"compression": "~1.7.4",
|
||||||
|
"country-flag-emoji-polyfill": "^0.1.8",
|
||||||
"croner": "~8.1.0",
|
"croner": "~8.1.0",
|
||||||
"dayjs": "~1.11.5",
|
"dayjs": "~1.11.5",
|
||||||
"dev-null": "^0.1.1",
|
"dev-null": "^0.1.1",
|
||||||
|
@ -7204,6 +7205,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/country-flag-emoji-polyfill": {
|
||||||
|
"version": "0.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/country-flag-emoji-polyfill/-/country-flag-emoji-polyfill-0.1.8.tgz",
|
||||||
|
"integrity": "sha512-Mbah52sADS3gshUYhK5142gtUuJpHYOXlXtLFI3Ly4RqgkmPMvhX9kMZSTqDM8P7UqtSW99eHKFphhQSGXA3Cg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cpu-features": {
|
"node_modules/cpu-features": {
|
||||||
"version": "0.0.10",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||||
|
|
|
@ -82,6 +82,7 @@
|
||||||
"command-exists": "~1.2.9",
|
"command-exists": "~1.2.9",
|
||||||
"compare-versions": "~3.6.0",
|
"compare-versions": "~3.6.0",
|
||||||
"compression": "~1.7.4",
|
"compression": "~1.7.4",
|
||||||
|
"country-flag-emoji-polyfill": "^0.1.8",
|
||||||
"croner": "~8.1.0",
|
"croner": "~8.1.0",
|
||||||
"dayjs": "~1.11.5",
|
"dayjs": "~1.11.5",
|
||||||
"dev-null": "^0.1.1",
|
"dev-null": "^0.1.1",
|
||||||
|
|
|
@ -380,39 +380,6 @@ class Monitor extends BeanModel {
|
||||||
if (await Monitor.isUnderMaintenance(this.id)) {
|
if (await Monitor.isUnderMaintenance(this.id)) {
|
||||||
bean.msg = "Monitor under maintenance";
|
bean.msg = "Monitor under maintenance";
|
||||||
bean.status = MAINTENANCE;
|
bean.status = MAINTENANCE;
|
||||||
} else if (this.type === "group") {
|
|
||||||
const children = await Monitor.getChildren(this.id);
|
|
||||||
|
|
||||||
if (children.length > 0) {
|
|
||||||
bean.status = UP;
|
|
||||||
bean.msg = "All children up and running";
|
|
||||||
for (const child of children) {
|
|
||||||
if (!child.active) {
|
|
||||||
// Ignore inactive childs
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const lastBeat = await Monitor.getPreviousHeartbeat(child.id);
|
|
||||||
|
|
||||||
// Only change state if the monitor is in worse conditions then the ones before
|
|
||||||
// lastBeat.status could be null
|
|
||||||
if (!lastBeat) {
|
|
||||||
bean.status = PENDING;
|
|
||||||
} else if (bean.status === UP && (lastBeat.status === PENDING || lastBeat.status === DOWN)) {
|
|
||||||
bean.status = lastBeat.status;
|
|
||||||
} else if (bean.status === PENDING && lastBeat.status === DOWN) {
|
|
||||||
bean.status = lastBeat.status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bean.status !== UP) {
|
|
||||||
bean.msg = "Child inaccessible";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set status pending if group is empty
|
|
||||||
bean.status = PENDING;
|
|
||||||
bean.msg = "Group empty";
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (this.type === "http" || this.type === "keyword" || this.type === "json-query") {
|
} else if (this.type === "http" || this.type === "keyword" || this.type === "json-query") {
|
||||||
// Do not do any queries/high loading things before the "bean.ping"
|
// Do not do any queries/high loading things before the "bean.ping"
|
||||||
let startTime = dayjs().valueOf();
|
let startTime = dayjs().valueOf();
|
||||||
|
@ -1625,7 +1592,7 @@ class Monitor extends BeanModel {
|
||||||
/**
|
/**
|
||||||
* Gets all Children of the monitor
|
* Gets all Children of the monitor
|
||||||
* @param {number} monitorID ID of monitor to get
|
* @param {number} monitorID ID of monitor to get
|
||||||
* @returns {Promise<LooseObject<any>>} Children
|
* @returns {Promise<LooseObject<any>[]>} Children
|
||||||
*/
|
*/
|
||||||
static async getChildren(monitorID) {
|
static async getChildren(monitorID) {
|
||||||
return await R.getAll(`
|
return await R.getAll(`
|
||||||
|
|
49
server/monitor-types/group.js
Normal file
49
server/monitor-types/group.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
const { UP, PENDING, DOWN } = require("../../src/util");
|
||||||
|
const { MonitorType } = require("./monitor-type");
|
||||||
|
const Monitor = require("../model/monitor");
|
||||||
|
|
||||||
|
class GroupMonitorType extends MonitorType {
|
||||||
|
name = "group";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async check(monitor, heartbeat, _server) {
|
||||||
|
const children = await Monitor.getChildren(monitor.id);
|
||||||
|
|
||||||
|
if (children.length > 0) {
|
||||||
|
heartbeat.status = UP;
|
||||||
|
heartbeat.msg = "All children up and running";
|
||||||
|
for (const child of children) {
|
||||||
|
if (!child.active) {
|
||||||
|
// Ignore inactive childs
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const lastBeat = await Monitor.getPreviousHeartbeat(child.id);
|
||||||
|
|
||||||
|
// Only change state if the monitor is in worse conditions then the ones before
|
||||||
|
// lastBeat.status could be null
|
||||||
|
if (!lastBeat) {
|
||||||
|
heartbeat.status = PENDING;
|
||||||
|
} else if (heartbeat.status === UP && (lastBeat.status === PENDING || lastBeat.status === DOWN)) {
|
||||||
|
heartbeat.status = lastBeat.status;
|
||||||
|
} else if (heartbeat.status === PENDING && lastBeat.status === DOWN) {
|
||||||
|
heartbeat.status = lastBeat.status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heartbeat.status !== UP) {
|
||||||
|
heartbeat.msg = "Child inaccessible";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set status pending if group is empty
|
||||||
|
heartbeat.status = PENDING;
|
||||||
|
heartbeat.msg = "Group empty";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
GroupMonitorType,
|
||||||
|
};
|
||||||
|
|
40
server/notification-providers/sms-planet.js
Normal file
40
server/notification-providers/sms-planet.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
class SMSPlanet extends NotificationProvider {
|
||||||
|
name = "SMSPlanet";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
const okMsg = "Sent Successfully.";
|
||||||
|
const url = "https://api2.smsplanet.pl/sms";
|
||||||
|
|
||||||
|
try {
|
||||||
|
let config = {
|
||||||
|
headers: {
|
||||||
|
"Authorization": "Bearer " + notification.smsplanetApiToken,
|
||||||
|
"content-type": "multipart/form-data"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
"from": notification.smsplanetSenderName,
|
||||||
|
"to": notification.smsplanetPhoneNumbers,
|
||||||
|
"msg": msg.replace(/🔴/, "❌")
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = await axios.post(url, data, config);
|
||||||
|
if (!response.data?.messageId) {
|
||||||
|
throw new Error(response.data?.errorMsg ?? "SMSPlanet server did not respond with the expected result");
|
||||||
|
}
|
||||||
|
|
||||||
|
return okMsg;
|
||||||
|
} catch (error) {
|
||||||
|
this.throwGeneralAxiosError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SMSPlanet;
|
|
@ -73,6 +73,7 @@ const Onesender = require("./notification-providers/onesender");
|
||||||
const Wpush = require("./notification-providers/wpush");
|
const Wpush = require("./notification-providers/wpush");
|
||||||
const SendGrid = require("./notification-providers/send-grid");
|
const SendGrid = require("./notification-providers/send-grid");
|
||||||
const YZJ = require("./notification-providers/yzj");
|
const YZJ = require("./notification-providers/yzj");
|
||||||
|
const SMSPlanet = require("./notification-providers/sms-planet");
|
||||||
|
|
||||||
class Notification {
|
class Notification {
|
||||||
|
|
||||||
|
@ -162,7 +163,8 @@ class Notification {
|
||||||
new Cellsynt(),
|
new Cellsynt(),
|
||||||
new Wpush(),
|
new Wpush(),
|
||||||
new SendGrid(),
|
new SendGrid(),
|
||||||
new YZJ()
|
new YZJ(),
|
||||||
|
new SMSPlanet(),
|
||||||
];
|
];
|
||||||
for (let item of list) {
|
for (let item of list) {
|
||||||
if (! item.name) {
|
if (! item.name) {
|
||||||
|
|
|
@ -89,7 +89,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
|
||||||
SELECT * FROM heartbeat
|
SELECT * FROM heartbeat
|
||||||
WHERE monitor_id = ?
|
WHERE monitor_id = ?
|
||||||
ORDER BY time DESC
|
ORDER BY time DESC
|
||||||
LIMIT 50
|
LIMIT 100
|
||||||
`, [
|
`, [
|
||||||
monitorID,
|
monitorID,
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -113,6 +113,7 @@ class UptimeKumaServer {
|
||||||
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
||||||
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
|
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
|
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
|
||||||
|
UptimeKumaServer.monitorTypeList["group"] = new GroupMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
|
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType();
|
UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType();
|
||||||
|
@ -551,6 +552,7 @@ const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor
|
||||||
const { TailscalePing } = require("./monitor-types/tailscale-ping");
|
const { TailscalePing } = require("./monitor-types/tailscale-ping");
|
||||||
const { DnsMonitorType } = require("./monitor-types/dns");
|
const { DnsMonitorType } = require("./monitor-types/dns");
|
||||||
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
||||||
|
const { GroupMonitorType } = require("./monitor-types/group");
|
||||||
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
||||||
const { MongodbMonitorType } = require("./monitor-types/mongodb");
|
const { MongodbMonitorType } = require("./monitor-types/mongodb");
|
||||||
const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq");
|
const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq");
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { setPageLocale } from "./util-frontend";
|
import { setPageLocale } from "./util-frontend";
|
||||||
|
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||||
export default {
|
export default {
|
||||||
created() {
|
created() {
|
||||||
setPageLocale();
|
setPageLocale();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
polyfillCountryFlagEmojis();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
@import "node_modules/bootstrap/scss/bootstrap";
|
@import "node_modules/bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
font-family: BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
|
font-family: "Twemoji Country Flags", BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
<router-link :to="monitorURL(monitor.id)" class="item" :class="{ 'disabled': ! monitor.active }">
|
<router-link :to="monitorURL(monitor.id)" class="item" :class="{ 'disabled': ! monitor.active }">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-9 col-md-8 small-padding" :class="{ 'monitor-item': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
|
<div class="col-6 small-padding" :class="{ 'monitor-item': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<Uptime :monitor="monitor" type="24" :pill="true" />
|
<Uptime :monitor="monitor" type="24" :pill="true" />
|
||||||
<span v-if="hasChildren" class="collapse-padding" @click.prevent="changeCollapsed">
|
<span v-if="hasChildren" class="collapse-padding" @click.prevent="changeCollapsed">
|
||||||
|
@ -22,11 +22,11 @@
|
||||||
</span>
|
</span>
|
||||||
{{ monitor.name }}
|
{{ monitor.name }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="monitor.tags.length > 0" class="tags">
|
<div v-if="monitor.tags.length > 0" class="tags gap-1">
|
||||||
<Tag v-for="tag in monitor.tags" :key="tag" :item="tag" :size="'sm'" />
|
<Tag v-for="tag in monitor.tags" :key="tag" :item="tag" :size="'sm'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
<div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-6">
|
||||||
<HeartbeatBar ref="heartbeatBar" size="small" :monitor-id="monitor.id" />
|
<HeartbeatBar ref="heartbeatBar" size="small" :monitor-id="monitor.id" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -186,7 +186,8 @@ export default {
|
||||||
"PushPlus": "PushPlus (推送加)",
|
"PushPlus": "PushPlus (推送加)",
|
||||||
"smsc": "SMSC",
|
"smsc": "SMSC",
|
||||||
"WPush": "WPush(wpush.cn)",
|
"WPush": "WPush(wpush.cn)",
|
||||||
"YZJ": "YZJ (云之家自定义机器人)"
|
"YZJ": "YZJ (云之家自定义机器人)",
|
||||||
|
"SMSPlanet": "SMSPlanet.pl"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sort by notification name
|
// Sort by notification name
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<template #item="monitor">
|
<template #item="monitor">
|
||||||
<div class="item" data-testid="monitor">
|
<div class="item" data-testid="monitor">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-9 col-md-8 small-padding">
|
<div class="col-6 small-padding">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
|
<font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
|
||||||
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
|
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
<div :key="$root.userHeartbeatBar" class="col-6">
|
||||||
<HeartbeatBar size="mid" :monitor-id="monitor.element.id" />
|
<HeartbeatBar size="mid" :monitor-id="monitor.element.id" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
'm-2': size == 'normal',
|
'm-2': size == 'normal',
|
||||||
'px-2': size == 'sm',
|
'px-2': size == 'sm',
|
||||||
'py-0': size == 'sm',
|
'py-0': size == 'sm',
|
||||||
'mx-1': size == 'sm',
|
|
||||||
}"
|
}"
|
||||||
:style="{ backgroundColor: item.color, fontSize: size == 'sm' ? '0.7em' : '1em' }"
|
:style="{ backgroundColor: item.color, fontSize: size == 'sm' ? '0.7em' : '1em' }"
|
||||||
>
|
>
|
||||||
|
@ -48,7 +47,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
displayText() {
|
displayText() {
|
||||||
if (this.item.value === "" || this.item.value === undefined) {
|
if (this.item.value === "" || this.item.value === undefined || this.item.value === null) {
|
||||||
return this.item.name;
|
return this.item.name;
|
||||||
} else {
|
} else {
|
||||||
return `${this.item.name}: ${this.item.value}`;
|
return `${this.item.name}: ${this.item.value}`;
|
||||||
|
|
46
src/components/notifications/SMSPlanet.vue
Normal file
46
src/components/notifications/SMSPlanet.vue
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="smsplanet-api-token" class="form-label">{{ $t('smsplanetApiToken') }}</label>
|
||||||
|
<HiddenInput id="smsplanet-api-token" v-model="$parent.notification.smsplanetApiToken" :required="true"></HiddenInput>
|
||||||
|
<i18n-t tag="div" keypath="smsplanetApiDocs" class="form-text">
|
||||||
|
<template #the_smsplanet_documentation>
|
||||||
|
<a
|
||||||
|
href="https://smsplanet.pl/doc/slate/index.html#introduction"
|
||||||
|
target="_blank"
|
||||||
|
>{{ $t("the smsplanet documentation") }}</a>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="smsplanet-phone-numbers" class="form-label">{{ $t("Phone numbers") }}</label>
|
||||||
|
<textarea
|
||||||
|
id="smsplanet-phone-numbers"
|
||||||
|
v-model="$parent.notification.smsplanetPhoneNumbers"
|
||||||
|
class="form-control"
|
||||||
|
:placeholder="smsplanetPhoneNumbers"
|
||||||
|
required
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="smsplanet-sender-name" class="form-label">{{ $t("Sender name") }}</label>
|
||||||
|
<input id="smsplanet-sender-name" v-model="$parent.notification.smsplanetSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||||
|
<div class="form-text">{{ $t("smsplanetNeedToApproveName") }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HiddenInput from "../HiddenInput.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
HiddenInput,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
smsplanetPhoneNumbers() {
|
||||||
|
return this.$t("Example:", [
|
||||||
|
"+48123456789,+48111222333",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -71,6 +71,7 @@ import WPush from "./WPush.vue";
|
||||||
import SIGNL4 from "./SIGNL4.vue";
|
import SIGNL4 from "./SIGNL4.vue";
|
||||||
import SendGrid from "./SendGrid.vue";
|
import SendGrid from "./SendGrid.vue";
|
||||||
import YZJ from "./YZJ.vue";
|
import YZJ from "./YZJ.vue";
|
||||||
|
import SMSPlanet from "./SMSPlanet.vue";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage all notification form.
|
* Manage all notification form.
|
||||||
|
@ -150,6 +151,7 @@ const NotificationFormList = {
|
||||||
"WPush": WPush,
|
"WPush": WPush,
|
||||||
"SendGrid": SendGrid,
|
"SendGrid": SendGrid,
|
||||||
"YZJ": YZJ,
|
"YZJ": YZJ,
|
||||||
|
"SMSPlanet": SMSPlanet,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NotificationFormList;
|
export default NotificationFormList;
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> {{ $t("Also check beta release") }}</label>
|
<label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> {{ $t("Also check beta release") }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-5">
|
||||||
|
<p>{{ $t("Font Twemoji by Twitter licensed under") }} <a href="https://creativecommons.org/licenses/by/4.0/">CC-BY 4.0</a></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1067,5 +1067,12 @@
|
||||||
"YZJ Robot Token": "YZJ Robot token",
|
"YZJ Robot Token": "YZJ Robot token",
|
||||||
"Plain Text": "Plain Text",
|
"Plain Text": "Plain Text",
|
||||||
"Message Template": "Message Template",
|
"Message Template": "Message Template",
|
||||||
"Template Format": "Template Format"
|
"Template Format": "Template Format",
|
||||||
|
"Font Twemoji by Twitter licensed under": "Font Twemoji by Twitter licensed under",
|
||||||
|
"smsplanetApiToken": "Token for the SMSPlanet API",
|
||||||
|
"smsplanetApiDocs": "Detailed information on obtaining API tokens can be found in {the_smsplanet_documentation}.",
|
||||||
|
"the smsplanet documentation": "the smsplanet documentation",
|
||||||
|
"Phone numbers": "Phone numbers",
|
||||||
|
"Sender name": "Sender name",
|
||||||
|
"smsplanetNeedToApproveName": "Needs to be approved in the client panel"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue