Merge branch 'master' into mqtt2

# Conflicts:
#	server/database.js
#	server/util-server.js
This commit is contained in:
Louis Lam
2022-04-17 19:46:33 +08:00
58 changed files with 456 additions and 168 deletions

View File

@@ -469,6 +469,10 @@ textarea.form-control {
color: $primary;
}
.prism-editor__textarea {
outline: none !important;
}
// Localization
@import "localization.scss";

View File

@@ -42,6 +42,7 @@ export default {
default: "No",
},
},
emits: [ "yes" ],
data: () => ({
modal: null,
}),

View File

@@ -57,6 +57,7 @@ export default {
default: undefined,
},
},
emits: [ "update:modelValue" ],
data() {
return {
visibility: "password",

View File

@@ -10,7 +10,7 @@ import { sleep } from "../util.ts";
export default {
props: {
value: [String, Number],
value: [ String, Number ],
time: {
type: Number,
default: 0.3,

View File

@@ -48,6 +48,7 @@ export default {
default: undefined,
},
},
emits: [ "update:modelValue" ],
data() {
return {
visibility: "password",

View File

@@ -78,7 +78,7 @@ export default {
Confirm,
},
props: {},
emits: ["added"],
emits: [ "added" ],
data() {
return {
model: null,

View File

@@ -220,6 +220,7 @@ export default {
if (newPeriod == "0") {
newPeriod = null;
this.heartbeatList = null;
this.$root.storage().removeItem(`chart-period-${this.monitorId}`);
} else {
this.loading = true;
@@ -228,6 +229,7 @@ export default {
toast.error(res.msg);
} else {
this.heartbeatList = res.data;
this.$root.storage()[`chart-period-${this.monitorId}`] = newPeriod;
}
this.loading = false;
});
@@ -248,6 +250,12 @@ export default {
},
{ deep: true }
);
// Load chart period from storage if saved
let period = this.$root.storage()[`chart-period-${this.monitorId}`];
if (period != null) {
this.chartPeriodHrs = Math.min(period, 6);
}
}
};
</script>

View File

@@ -105,7 +105,7 @@ export default {
Confirm,
},
props: {},
emits: ["added"],
emits: [ "added" ],
data() {
return {
model: null,

View File

@@ -0,0 +1,19 @@
<template>
<div class="mb-3">
<label for="pushdeer-key" class="form-label">{{ $t("PushDeer Key") }}</label>
<HiddenInput id="pushdeer-key" v-model="$parent.notification.pushdeerKey" :required="true" autocomplete="one-time-code" placeholder="PDUxxxx"></HiddenInput>
</div>
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
<a href="http://www.pushdeer.com/" rel="noopener noreferrer" target="_blank">http://www.pushdeer.com/</a>
</i18n-t>
</template>
<script>
import HiddenInput from "../HiddenInput.vue";
export default {
components: {
HiddenInput,
},
};
</script>

View File

@@ -30,6 +30,7 @@ import GoogleChat from "./GoogleChat.vue";
import Gorush from "./Gorush.vue";
import Alerta from "./Alerta.vue";
import OneBot from "./OneBot.vue";
import PushDeer from "./PushDeer.vue";
/**
* Manage all notification form.
@@ -69,6 +70,7 @@ const NotificationFormList = {
"gorush": Gorush,
"alerta": Alerta,
"OneBot": OneBot,
"PushDeer": PushDeer,
};
export default NotificationFormList;

View File

@@ -43,7 +43,7 @@ for (let lang in languageList) {
};
}
const rtlLangs = ["fa"];
const rtlLangs = [ "fa" ];
export const currentLocale = () => localStorage.locale
|| languageList[navigator.language] && navigator.language

View File

@@ -197,7 +197,7 @@ export default {
line: "Line Messenger",
mattermost: "Mattermost",
"Status Page": "Статус страница",
"Status Pages": "Статус страница",
"Status Pages": "Статус страници",
"Primary Base URL": "Основен базов URL адрес",
"Push URL": "Генериран Push URL адрес",
needPushEvery: "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди",
@@ -371,4 +371,75 @@ export default {
alertaAlertState: "Състояние на тревога",
alertaRecoverState: "Състояние на възстановяване",
deleteStatusPageMsg: "Сигурни ли сте, че желаете да изтриете тази статус страница?",
Proxies: "Проксита",
default: "По подразбиране",
enabled: "Включено",
setAsDefault: "Зададен по подразбиране",
deleteProxyMsg: "Сигурни ли сте, че желаете да изтриете това прокси за всички монитори?",
proxyDescription: "Прокситата трябва да бъдат зададени към монитор за да функционират.",
enableProxyDescription: "Това прокси няма да има ефект върху заявките за мониторинг, докато не бъде активирано. Може да контролирате временното деактивиране на проксито от всички монитори чрез статуса на активиране.",
setAsDefaultProxyDescription: "Това проки ще бъде включено по подразбиране за новите монитори. Може да го изключите по отделно за всеки един монитор.",
"Certificate Chain": "Верига на сертификата",
Valid: "Валиден",
Invalid: "Невалиден",
AccessKeyId: "ID на ключ за достъп",
SecretAccessKey: "Тайна на ключа за достъп",
PhoneNumbers: "Телефонни номера",
TemplateCode: "Шаблон Код",
SignName: "Знак име",
"Sms template must contain parameters: ": "SMS шаблонът трябва да съдържа следните параметри: ",
"Bark Endpoint": "Bark крайна точка",
WebHookUrl: "URL адрес на уеб кука",
SecretKey: "Таен ключ",
"For safety, must use secret key": "За сигурност, трябва да се използва таен ключ",
"Device Token": "Токен за устройство",
Platform: "Платформа",
iOS: "iOS",
Android: "Android",
Huawei: "Huawei",
High: "Висок",
Retry: "Повтори",
Topic: "Тема",
"WeCom Bot Key": "WeCom бот ключ",
"Setup Proxy": "Настройка за прокси",
"Proxy Protocol": "Прокси протокол",
"Proxy Server": "Прокси сървър",
"Proxy server has authentication": "Прокси сървърът е с удостоверяване",
User: "Потребител",
Installed: "Инсталиран",
"Not installed": "Не е инсталиран",
Running: "Работи",
"Not running": "Не работи",
"Remove Token": "Премахни токен",
Start: "Старт",
Stop: "Стоп",
"Uptime Kuma": "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": "Без прокси",
"HTTP Basic Auth": "HTTP основно удостоверяване",
"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 Tunnel\". Сигурни ли сте, че желаете да го спрете? Въведете Вашата текуща парола за да потвърдите.",
"Other Software": "Друг софтуер",
"For example: nginx, Apache and Traefik.": "Например: Nginx, Apache и Traefik.",
"Please read": "Моля, прочетете",
"Subject:": "Тема:",
"Valid To:": "Валиден до:",
"Days Remaining:": "Оставащи дни:",
"Issuer:": "Издател:",
"Fingerprint:": "Пръстов отпечатък:",
"No status pages": "Няма статус страници",
};

View File

@@ -337,7 +337,7 @@ export default {
"Hide Tags": "Tags ausblenden",
Description: "Beschreibung",
"No monitors available.": "Keine Monitore verfügbar.",
"Add one": "Füge eins hinzu",
"Add one": "Hinzufügen",
"No Monitors": "Keine Monitore",
"Untitled Group": "Gruppe ohne Titel",
Services: "Dienste",
@@ -442,4 +442,7 @@ export default {
"Issuer:": "Aussteller:",
"Fingerprint:": "Fingerabdruck:",
"No status pages": "Keine Status-Seiten",
Customize: "Anpassen",
"Custom Footer": "Eigener Footer",
"Custom CSS": "Eigenes CSS",
};

View File

@@ -358,6 +358,9 @@ export default {
serwersmsPhoneNumber: "Phone number",
serwersmsSenderName: "SMS Sender Name (registered via customer portal)",
stackfield: "Stackfield",
Customize: "Customize",
"Custom Footer": "Custom Footer",
"Custom CSS": "Custom CSS",
smtpDkimSettings: "DKIM Settings",
smtpDkimDesc: "Please refer to the Nodemailer DKIM {0} for usage.",
documentation: "documentation",
@@ -455,4 +458,5 @@ export default {
onebotPrivateMessage: "Private",
onebotUserOrGroupId: "Group/User ID",
onebotSafetyTips: "For safety, must set access token",
"PushDeer Key": "PushDeer Key",
};

View File

@@ -1,6 +1,6 @@
import { io } from "socket.io-client";
import { useToast } from "vue-toastification";
import jwt_decode from "jwt-decode";
import jwtDecode from "jwt-decode";
import Favico from "favico.js";
const toast = useToast();
@@ -89,7 +89,7 @@ export default {
}
socket = io(wsHost, {
transports: ["websocket"],
transports: [ "websocket" ],
});
socket.on("info", (info) => {
@@ -108,7 +108,7 @@ export default {
socket.on("monitorList", (data) => {
// Add Helper function
Object.entries(data).forEach(([monitorID, monitor]) => {
Object.entries(data).forEach(([ monitorID, monitor ]) => {
monitor.getUrl = () => {
try {
return new URL(monitor.url);
@@ -266,7 +266,7 @@ export default {
const jwtToken = this.$root.storage().token;
if (jwtToken && jwtToken !== "autoLogin") {
return jwt_decode(jwtToken);
return jwtDecode(jwtToken);
}
return undefined;
},

View File

@@ -426,17 +426,17 @@ export default {
},
bodyPlaceholder() {
return this.$t("Example:", [`
return this.$t("Example:", [ `
{
"key": "value"
}`]);
}` ]);
},
headersPlaceholder() {
return this.$t("Example:", [`
return this.$t("Example:", [ `
{
"HeaderName": "HeaderValue"
}`]);
}` ]);
}
},
@@ -521,7 +521,7 @@ export default {
upsideDown: false,
expiryNotification: false,
maxredirects: 10,
accepted_statuscodes: ["200-299"],
accepted_statuscodes: [ "200-299" ],
dns_resolve_type: "A",
dns_resolve_server: "1.1.1.1",
proxyId: null,

View File

@@ -16,11 +16,18 @@
<input id="title" v-model="config.title" type="text" class="form-control">
</div>
<!-- Description -->
<div class="my-3">
<label for="description" class="form-label">{{ $t("Description") }}</label>
<textarea id="description" v-model="config.description" class="form-control"></textarea>
</div>
<!-- Footer Text -->
<div class="my-3">
<label for="footer-text" class="form-label">{{ $t("Footer Text") }}</label>
<textarea id="footer-text" v-model="config.footerText" class="form-control"></textarea>
</div>
<div class="my-3 form-check form-switch">
<input id="switch-theme" v-model="config.theme" class="form-check-input" type="checkbox" true-value="dark" false-value="light">
<label class="form-check-label" for="switch-theme">{{ $t("Switch to Dark Theme") }}</label>
@@ -31,6 +38,12 @@
<label class="form-check-label" for="showTags">{{ $t("Show Tags") }}</label>
</div>
<!-- Show Powered By -->
<div class="my-3 form-check form-switch">
<input id="show-powered-by" v-model="config.showPoweredBy" class="form-check-input" type="checkbox">
<label class="form-check-label" for="show-powered-by">{{ $t("Show Powered By") }}</label>
</div>
<div v-if="false" class="my-3">
<label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label>
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
@@ -51,6 +64,12 @@
</ul>
</div>
<!-- Custom CSS -->
<div class="my-3">
<div class="mb-1">{{ $t("Custom CSS") }}</div>
<prism-editor v-model="config.customCSS" class="css-editor" :highlight="highlighter" line-numbers></prism-editor>
</div>
<div class="danger-zone">
<button class="btn btn-danger me-2" @click="deleteDialog">
<font-awesome-icon icon="trash" />
@@ -239,13 +258,24 @@
</div>
<footer class="mt-5 mb-4">
{{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
<div class="custom-footer-text text-start">
<strong v-if="enableEditMode">{{ $t("Custom Footer") }}:</strong>
</div>
<Editable v-model="config.footerText" tag="div" :contenteditable="enableEditMode" :noNL="false" class="alert-heading p-2" />
<p v-if="config.showPoweredBy">
{{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
</p>
</footer>
</div>
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteStatusPage">
{{ $t("deleteStatusPageMsg") }}
</Confirm>
<component is="style" v-if="config.customCSS" type="text/css">
{{ config.customCSS }}
</component>
</div>
</template>
@@ -259,11 +289,20 @@ import dayjs from "dayjs";
import Favico from "favico.js";
import { getResBaseURL } from "../util-frontend";
import Confirm from "../components/Confirm.vue";
// import Prism Editor
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
// import highlighting library (you can use any library you want just return html string)
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-css";
import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles
const toast = useToast();
const leavePageMsg = "Do you really want to leave? you have unsaved changes!";
// eslint-disable-next-line no-unused-vars
let feedInterval;
const favicon = new Favico({
@@ -276,6 +315,7 @@ export default {
PublicGroupList,
ImageCropUpload,
Confirm,
PrismEditor,
},
// Leave Page for vue route change
@@ -418,6 +458,13 @@ export default {
this.$root.getSocket().emit("getStatusPage", this.slug, (res) => {
if (res.ok) {
this.config = res.config;
if (!this.config.customCSS) {
this.config.customCSS = "body {\n" +
" \n" +
"}\n";
}
} else {
toast.error(res.msg);
}
@@ -520,6 +567,10 @@ export default {
},
methods: {
highlighter(code) {
return highlight(code, languages.css);
},
updateHeartbeatList() {
// If editMode, it will use the data from websocket.
if (! this.editMode) {
@@ -893,4 +944,18 @@ footer {
}
}
/* required class */
.css-editor {
/* we dont use `language-` classes anymore so thats why we need to add background and text color manually */
border-radius: 1rem;
padding: 10px 5px;
border: 1px solid #ced4da;
.dark & {
background: $dark-bg;
border: 1px solid $dark-border-color;
}
}
</style>