mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-01 03:49:24 +08:00 
			
		
		
		
	WIP: Convert Settings to components
This commit is contained in:
		| @@ -12,6 +12,7 @@ $dark-font-color2: #020b05; | ||||
| $dark-bg: #0d1117; | ||||
| $dark-bg2: #070a10; | ||||
| $dark-border-color: #1d2634; | ||||
| $dark-header-bg: #161b22; | ||||
|  | ||||
| $easing-in: cubic-bezier(0.54, 0.78, 0.55, 0.97); | ||||
| $easing-out: cubic-bezier(0.25, 0.46, 0.45, 0.94); | ||||
|   | ||||
| @@ -137,7 +137,7 @@ export default { | ||||
|     justify-content: space-between; | ||||
|  | ||||
|     .dark & { | ||||
|         background-color: #161b22; | ||||
|         background-color: $dark-header-bg; | ||||
|         border-bottom: 0; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										25
									
								
								src/components/settings/About.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/components/settings/About.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| <template> | ||||
|     <div class="d-flex justify-content-center align-items-center"> | ||||
|         <div class="logo d-flex flex-column justify-content-center align-items-center"> | ||||
|             <object class="my-4" width="200" height="200" data="/icon.svg" /> | ||||
|             <div class="fs-4 fw-bold">Uptime Kuma</div> | ||||
|             <div>{{ $t("Version") }}: {{ $root.info.version }}</div> | ||||
|             <div class="my-1 update-link"><a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">{{ $t("Check Update On GitHub") }}</a></div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|  | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .logo { | ||||
|     margin: 4em 1em; | ||||
| } | ||||
| .update-link { | ||||
|     font-size: 0.9em; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										149
									
								
								src/components/settings/Appearance.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/components/settings/Appearance.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <div class="my-4"> | ||||
|             <label for="language" class="form-label"> | ||||
|                 {{ $t("Language") }} | ||||
|             </label> | ||||
|             <select id="language" v-model="$i18n.locale" class="form-select"> | ||||
|                 <option | ||||
|                     v-for="(lang, i) in $i18n.availableLocales" | ||||
|                     :key="`Lang${i}`" | ||||
|                     :value="lang" | ||||
|                 > | ||||
|                     {{ $i18n.messages[lang].languageName }} | ||||
|                 </option> | ||||
|             </select> | ||||
|         </div> | ||||
|         <div class="my-4"> | ||||
|             <label for="timezone" class="form-label">{{ $t("Theme") }}</label> | ||||
|             <div> | ||||
|                 <div | ||||
|                     class="btn-group" | ||||
|                     role="group" | ||||
|                     aria-label="Basic checkbox toggle button group" | ||||
|                 > | ||||
|                     <input | ||||
|                         id="btncheck1" | ||||
|                         v-model="$root.userTheme" | ||||
|                         type="radio" | ||||
|                         class="btn-check" | ||||
|                         name="theme" | ||||
|                         autocomplete="off" | ||||
|                         value="light" | ||||
|                     /> | ||||
|                     <label class="btn btn-outline-primary" for="btncheck1"> | ||||
|                         {{ $t("Light") }} | ||||
|                     </label> | ||||
|  | ||||
|                     <input | ||||
|                         id="btncheck2" | ||||
|                         v-model="$root.userTheme" | ||||
|                         type="radio" | ||||
|                         class="btn-check" | ||||
|                         name="theme" | ||||
|                         autocomplete="off" | ||||
|                         value="dark" | ||||
|                     /> | ||||
|                     <label class="btn btn-outline-primary" for="btncheck2"> | ||||
|                         {{ $t("Dark") }} | ||||
|                     </label> | ||||
|  | ||||
|                     <input | ||||
|                         id="btncheck3" | ||||
|                         v-model="$root.userTheme" | ||||
|                         type="radio" | ||||
|                         class="btn-check" | ||||
|                         name="theme" | ||||
|                         autocomplete="off" | ||||
|                         value="auto" | ||||
|                     /> | ||||
|                     <label class="btn btn-outline-primary" for="btncheck3"> | ||||
|                         {{ $t("Auto") }} | ||||
|                     </label> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="my-4"> | ||||
|             <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label> | ||||
|             <div> | ||||
|                 <div | ||||
|                     class="btn-group" | ||||
|                     role="group" | ||||
|                     aria-label="Basic checkbox toggle button group" | ||||
|                 > | ||||
|                     <input | ||||
|                         id="btncheck4" | ||||
|                         v-model="$root.userHeartbeatBar" | ||||
|                         type="radio" | ||||
|                         class="btn-check" | ||||
|                         name="heartbeatBarTheme" | ||||
|                         autocomplete="off" | ||||
|                         value="normal" | ||||
|                     /> | ||||
|                     <label class="btn btn-outline-primary" for="btncheck4"> | ||||
|                         {{ $t("Normal") }} | ||||
|                     </label> | ||||
|  | ||||
|                     <input | ||||
|                         id="btncheck5" | ||||
|                         v-model="$root.userHeartbeatBar" | ||||
|                         type="radio" | ||||
|                         class="btn-check" | ||||
|                         name="heartbeatBarTheme" | ||||
|                         autocomplete="off" | ||||
|                         value="bottom" | ||||
|                     /> | ||||
|                     <label class="btn btn-outline-primary" for="btncheck5"> | ||||
|                         {{ $t("Bottom") }} | ||||
|                     </label> | ||||
|  | ||||
|                     <input | ||||
|                         id="btncheck6" | ||||
|                         v-model="$root.userHeartbeatBar" | ||||
|                         type="radio" | ||||
|                         class="btn-check" | ||||
|                         name="heartbeatBarTheme" | ||||
|                         autocomplete="off" | ||||
|                         value="none" | ||||
|                     /> | ||||
|                     <label class="btn btn-outline-primary" for="btncheck6"> | ||||
|                         {{ $t("None") }} | ||||
|                     </label> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { setPageLocale } from "../../util-frontend"; | ||||
| export default { | ||||
|     watch: { | ||||
|         "$i18n.locale"() { | ||||
|             localStorage.locale = this.$i18n.locale; | ||||
|             setPageLocale(); | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "../../assets/vars.scss"; | ||||
|  | ||||
| .btn-check:active + .btn-outline-primary, | ||||
| .btn-check:checked + .btn-outline-primary, | ||||
| .btn-check:hover + .btn-outline-primary { | ||||
|     color: #fff; | ||||
|  | ||||
|     .dark & { | ||||
|         color: #000; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .dark { | ||||
|     .list-group-item { | ||||
|         background-color: $dark-bg2; | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										213
									
								
								src/components/settings/Backup.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								src/components/settings/Backup.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <div class="my-4"> | ||||
|             <h4 class="mt-4 mb-2">{{ $t("Export Backup") }}</h4> | ||||
|  | ||||
|             <p> | ||||
|                 {{ $t("backupDescription") }} <br /> | ||||
|                 ({{ $t("backupDescription2") }}) <br /> | ||||
|             </p> | ||||
|  | ||||
|             <div class="mb-2"> | ||||
|                 <button class="btn btn-primary" @click="downloadBackup"> | ||||
|                     {{ $t("Export") }} | ||||
|                 </button> | ||||
|             </div> | ||||
|  | ||||
|             <p> | ||||
|                 <strong>{{ $t("backupDescription3") }}</strong> | ||||
|             </p> | ||||
|         </div> | ||||
|  | ||||
|         <div class="my-4"> | ||||
|             <h4 class="mt-4 mb-2">{{ $t("Import Backup") }}</h4> | ||||
|  | ||||
|             <label class="form-label">{{ $t("Options") }}:</label> | ||||
|             <br /> | ||||
|             <div class="form-check form-check-inline"> | ||||
|                 <input | ||||
|                     id="radioKeep" | ||||
|                     v-model="importHandle" | ||||
|                     class="form-check-input" | ||||
|                     type="radio" | ||||
|                     name="radioImportHandle" | ||||
|                     value="keep" | ||||
|                 /> | ||||
|                 <label class="form-check-label" for="radioKeep"> | ||||
|                     {{ $t("Keep both") }} | ||||
|                 </label> | ||||
|             </div> | ||||
|             <div class="form-check form-check-inline"> | ||||
|                 <input | ||||
|                     id="radioSkip" | ||||
|                     v-model="importHandle" | ||||
|                     class="form-check-input" | ||||
|                     type="radio" | ||||
|                     name="radioImportHandle" | ||||
|                     value="skip" | ||||
|                 /> | ||||
|                 <label class="form-check-label" for="radioSkip"> | ||||
|                     {{ $t("Skip existing") }} | ||||
|                 </label> | ||||
|             </div> | ||||
|             <div class="form-check form-check-inline"> | ||||
|                 <input | ||||
|                     id="radioOverwrite" | ||||
|                     v-model="importHandle" | ||||
|                     class="form-check-input" | ||||
|                     type="radio" | ||||
|                     name="radioImportHandle" | ||||
|                     value="overwrite" | ||||
|                 /> | ||||
|                 <label class="form-check-label" for="radioOverwrite"> | ||||
|                     {{ $t("Overwrite") }} | ||||
|                 </label> | ||||
|             </div> | ||||
|             <div class="form-text mb-2"> | ||||
|                 {{ $t("importHandleDescription") }} | ||||
|             </div> | ||||
|  | ||||
|             <div class="mb-2"> | ||||
|                 <input | ||||
|                     id="importBackup" | ||||
|                     type="file" | ||||
|                     class="form-control" | ||||
|                     accept="application/json" | ||||
|                 /> | ||||
|             </div> | ||||
|  | ||||
|             <div class="input-group mb-2 justify-content-end"> | ||||
|                 <button | ||||
|                     type="button" | ||||
|                     class="btn btn-outline-primary" | ||||
|                     :disabled="processing" | ||||
|                     @click="confirmImport" | ||||
|                 > | ||||
|                     <div | ||||
|                         v-if="processing" | ||||
|                         class="spinner-border spinner-border-sm me-1" | ||||
|                     ></div> | ||||
|                     {{ $t("Import") }} | ||||
|                 </button> | ||||
|             </div> | ||||
|  | ||||
|             <div | ||||
|                 v-if="importAlert" | ||||
|                 class="alert alert-danger mt-3" | ||||
|                 style="padding: 6px 16px" | ||||
|             > | ||||
|                 {{ importAlert }} | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <Confirm | ||||
|             ref="confirmImport" | ||||
|             btn-style="btn-danger" | ||||
|             :yes-text="$t('Yes')" | ||||
|             :no-text="$t('No')" | ||||
|             @yes="importBackup" | ||||
|         > | ||||
|             {{ $t("confirmImportMsg") }} | ||||
|         </Confirm> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Confirm from "../../components/Confirm.vue"; | ||||
| import dayjs from "dayjs"; | ||||
| import { useToast } from "vue-toastification"; | ||||
|  | ||||
| const toast = useToast(); | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         Confirm, | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|         return { | ||||
|             processing: false, | ||||
|             importHandle: "skip", | ||||
|             importAlert: null, | ||||
|         }; | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|         confirmImport() { | ||||
|             this.$refs.confirmImport.show(); | ||||
|         }, | ||||
|  | ||||
|         downloadBackup() { | ||||
|             let time = dayjs().format("YYYY_MM_DD-hh_mm_ss"); | ||||
|             let fileName = `Uptime_Kuma_Backup_${time}.json`; | ||||
|             let monitorList = Object.values(this.$root.monitorList); | ||||
|             let exportData = { | ||||
|                 version: this.$root.info.version, | ||||
|                 notificationList: this.$root.notificationList, | ||||
|                 monitorList: monitorList, | ||||
|             }; | ||||
|             exportData = JSON.stringify(exportData, null, 4); | ||||
|             let downloadItem = document.createElement("a"); | ||||
|             downloadItem.setAttribute( | ||||
|                 "href", | ||||
|                 "data:application/json;charset=utf-8," + | ||||
|                     encodeURIComponent(exportData) | ||||
|             ); | ||||
|             downloadItem.setAttribute("download", fileName); | ||||
|             downloadItem.click(); | ||||
|         }, | ||||
|  | ||||
|         importBackup() { | ||||
|             this.processing = true; | ||||
|             let uploadItem = document.getElementById("importBackup").files; | ||||
|  | ||||
|             if (uploadItem.length <= 0) { | ||||
|                 this.processing = false; | ||||
|                 return (this.importAlert = this.$t("alertNoFile")); | ||||
|             } | ||||
|  | ||||
|             if (uploadItem.item(0).type !== "application/json") { | ||||
|                 this.processing = false; | ||||
|                 return (this.importAlert = this.$t("alertWrongFileType")); | ||||
|             } | ||||
|  | ||||
|             let fileReader = new FileReader(); | ||||
|             fileReader.readAsText(uploadItem.item(0)); | ||||
|  | ||||
|             fileReader.onload = (item) => { | ||||
|                 this.$root.uploadBackup( | ||||
|                     item.target.result, | ||||
|                     this.importHandle, | ||||
|                     (res) => { | ||||
|                         this.processing = false; | ||||
|  | ||||
|                         if (res.ok) { | ||||
|                             toast.success(res.msg); | ||||
|                         } else { | ||||
|                             toast.error(res.msg); | ||||
|                         } | ||||
|                     } | ||||
|                 ); | ||||
|             }; | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "../../assets/vars.scss"; | ||||
|  | ||||
| .dark { | ||||
|     #importBackup { | ||||
|         &::file-selector-button { | ||||
|             color: $primary; | ||||
|             background-color: $dark-bg; | ||||
|         } | ||||
|  | ||||
|         &:hover:not(:disabled):not([readonly])::file-selector-button { | ||||
|             color: $dark-font-color2; | ||||
|             background-color: $primary; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										191
									
								
								src/components/settings/General.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/components/settings/General.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <form class="my-4" @submit.prevent="saveGeneral"> | ||||
|             <!-- Timezone --> | ||||
|             <div class="mb-4"> | ||||
|                 <label for="timezone" class="form-label"> | ||||
|                     {{ $t("Timezone") }} | ||||
|                 </label> | ||||
|                 <select id="timezone" v-model="$root.userTimezone" class="form-select"> | ||||
|                     <option value="auto"> | ||||
|                         {{ $t("Auto") }}: {{ guessTimezone }} | ||||
|                     </option> | ||||
|                     <option | ||||
|                         v-for="(timezone, index) in timezoneList" | ||||
|                         :key="index" | ||||
|                         :value="timezone.value" | ||||
|                     > | ||||
|                         {{ timezone.name }} | ||||
|                     </option> | ||||
|                 </select> | ||||
|             </div> | ||||
|  | ||||
|             <!-- Search Engine --> | ||||
|             <div class="mb-4"> | ||||
|                 <label class="form-label"> | ||||
|                     {{ $t("Search Engine Visibility") }} | ||||
|                 </label> | ||||
|  | ||||
|                 <div class="form-check"> | ||||
|                     <input | ||||
|                         id="searchEngineIndexYes" | ||||
|                         v-model="settings.searchEngineIndex" | ||||
|                         class="form-check-input" | ||||
|                         type="radio" | ||||
|                         name="flexRadioDefault" | ||||
|                         :value="true" | ||||
|                         required | ||||
|                     /> | ||||
|                     <label class="form-check-label" for="searchEngineIndexYes"> | ||||
|                         {{ $t("Allow indexing") }} | ||||
|                     </label> | ||||
|                 </div> | ||||
|                 <div class="form-check"> | ||||
|                     <input | ||||
|                         id="searchEngineIndexNo" | ||||
|                         v-model="settings.searchEngineIndex" | ||||
|                         class="form-check-input" | ||||
|                         type="radio" | ||||
|                         name="flexRadioDefault" | ||||
|                         :value="false" | ||||
|                         required | ||||
|                     /> | ||||
|                     <label class="form-check-label" for="searchEngineIndexNo"> | ||||
|                         {{ $t("Discourage search engines from indexing site") }} | ||||
|                     </label> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <!-- Entry Page --> | ||||
|             <div class="mb-4"> | ||||
|                 <label class="form-label">{{ $t("Entry Page") }}</label> | ||||
|  | ||||
|                 <div class="form-check"> | ||||
|                     <input | ||||
|                         id="entryPageYes" | ||||
|                         v-model="settings.entryPage" | ||||
|                         class="form-check-input" | ||||
|                         type="radio" | ||||
|                         name="statusPage" | ||||
|                         value="dashboard" | ||||
|                         required | ||||
|                     /> | ||||
|                     <label class="form-check-label" for="entryPageYes"> | ||||
|                         {{ $t("Dashboard") }} | ||||
|                     </label> | ||||
|                 </div> | ||||
|  | ||||
|                 <div class="form-check"> | ||||
|                     <input | ||||
|                         id="entryPageNo" | ||||
|                         v-model="settings.entryPage" | ||||
|                         class="form-check-input" | ||||
|                         type="radio" | ||||
|                         name="statusPage" | ||||
|                         value="statusPage" | ||||
|                         required | ||||
|                     /> | ||||
|                     <label class="form-check-label" for="entryPageNo"> | ||||
|                         {{ $t("Status Page") }} | ||||
|                     </label> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <!-- Primary Base URL --> | ||||
|             <div class="mb-4"> | ||||
|                 <label class="form-label" for="primaryBaseURL"> | ||||
|                     {{ $t("Primary Base URL") }} | ||||
|                 </label> | ||||
|  | ||||
|                 <div class="input-group mb-3"> | ||||
|                     <input | ||||
|                         id="primaryBaseURL" | ||||
|                         v-model="settings.primaryBaseURL" | ||||
|                         class="form-control" | ||||
|                         name="primaryBaseURL" | ||||
|                         placeholder="https://" | ||||
|                         pattern="https?://.+" | ||||
|                     /> | ||||
|                     <button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL"> | ||||
|                         {{ $t("Auto Get") }} | ||||
|                     </button> | ||||
|                 </div> | ||||
|  | ||||
|                 <div class="form-text"></div> | ||||
|             </div> | ||||
|  | ||||
|             <!-- Steam API Key --> | ||||
|             <div class="mb-4"> | ||||
|                 <label class="form-label" for="steamAPIKey"> | ||||
|                     {{ $t("Steam API Key") }} | ||||
|                 </label> | ||||
|                 <HiddenInput | ||||
|                     id="steamAPIKey" | ||||
|                     v-model="settings.steamAPIKey" | ||||
|                 /> | ||||
|                 <div class="form-text"> | ||||
|                     {{ $t("steamApiKeyDescription") }} | ||||
|                     <a href="https://steamcommunity.com/dev" target="_blank"> | ||||
|                         https://steamcommunity.com/dev | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <!-- Save Button --> | ||||
|             <div> | ||||
|                 <button class="btn btn-primary" type="submit"> | ||||
|                     {{ $t("Save") }} | ||||
|                 </button> | ||||
|             </div> | ||||
|         </form> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import HiddenInput from "../../components/HiddenInput.vue"; | ||||
| import dayjs from "dayjs"; | ||||
| import utc from "dayjs/plugin/utc"; | ||||
| import timezone from "dayjs/plugin/timezone"; | ||||
| import { timezoneList } from "../../util-frontend"; | ||||
| dayjs.extend(utc); | ||||
| dayjs.extend(timezone); | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         HiddenInput, | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|         return { | ||||
|             timezoneList: timezoneList(), | ||||
|         }; | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|         settings() { | ||||
|             return this.$parent.$parent.settings; | ||||
|         }, | ||||
|         saveSettings() { | ||||
|             return this.$parent.$parent.saveSettings; | ||||
|         }, | ||||
|         settingsLoaded() { | ||||
|             return this.$parent.$parent.settingsLoaded; | ||||
|         }, | ||||
|         guessTimezone() { | ||||
|             return dayjs.tz.guess(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|         saveGeneral() { | ||||
|             localStorage.timezone = this.$root.userTimezone; | ||||
|             this.saveSettings(); | ||||
|         }, | ||||
|         autoGetPrimaryBaseURL() { | ||||
|             this.settings.primaryBaseURL = location.protocol + "//" + location.host; | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style></style> | ||||
							
								
								
									
										127
									
								
								src/components/settings/MonitorHistory.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/components/settings/MonitorHistory.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <div class="my-4"> | ||||
|             <label for="keepDataPeriodDays" class="form-label"> | ||||
|                 {{ | ||||
|                     $t("clearDataOlderThan", [ | ||||
|                         settings.keepDataPeriodDays, | ||||
|                     ]) | ||||
|                 }} | ||||
|             </label> | ||||
|             <input | ||||
|                 id="keepDataPeriodDays" | ||||
|                 v-model="settings.keepDataPeriodDays" | ||||
|                 type="number" | ||||
|                 class="form-control" | ||||
|                 required | ||||
|                 min="1" | ||||
|                 step="1" | ||||
|             /> | ||||
|         </div> | ||||
|         <div class="my-4"> | ||||
|             <div class="my-3"> | ||||
|                 <button class="btn btn-outline-info me-2" @click="shrinkDatabase"> | ||||
|                     {{ $t("Shrink Database") }} ({{ databaseSizeDisplay }}) | ||||
|                 </button> | ||||
|                 <div class="form-text ms-2">{{ $t("shrinkDatabaseDescription") }}</div> | ||||
|             </div> | ||||
|             <button | ||||
|                 class="btn btn-outline-danger me-2 mb-2" | ||||
|                 @click="confirmClearStatistics" | ||||
|             > | ||||
|                 {{ $t("Clear all statistics") }} | ||||
|             </button> | ||||
|         </div> | ||||
|         <Confirm | ||||
|             ref="confirmClearStatistics" | ||||
|             btn-style="btn-danger" | ||||
|             :yes-text="$t('Yes')" | ||||
|             :no-text="$t('No')" | ||||
|             @yes="clearStatistics" | ||||
|         > | ||||
|             {{ $t("confirmClearStatisticsMsg") }} | ||||
|         </Confirm> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Confirm from "../../components/Confirm.vue"; | ||||
| import { debug } from "../../util.ts"; | ||||
| import { useToast } from "vue-toastification"; | ||||
|  | ||||
| const toast = useToast(); | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         Confirm, | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|         return { | ||||
|             databaseSize: 0, | ||||
|         }; | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|         settings() { | ||||
|             return this.$parent.$parent.settings; | ||||
|         }, | ||||
|         saveSettings() { | ||||
|             return this.$parent.$parent.saveSettings; | ||||
|         }, | ||||
|         settingsLoaded() { | ||||
|             return this.$parent.$parent.settingsLoaded; | ||||
|         }, | ||||
|         databaseSizeDisplay() { | ||||
|             return ( | ||||
|                 Math.round((this.databaseSize / 1024 / 1024) * 10) / 10 + " MB" | ||||
|             ); | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     mounted() { | ||||
|         this.loadDatabaseSize(); | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|         loadDatabaseSize() { | ||||
|             debug("load database size"); | ||||
|             this.$root.getSocket().emit("getDatabaseSize", (res) => { | ||||
|                 if (res.ok) { | ||||
|                     this.databaseSize = res.size; | ||||
|                     debug("database size: " + res.size); | ||||
|                 } else { | ||||
|                     debug(res); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         shrinkDatabase() { | ||||
|             this.$root.getSocket().emit("shrinkDatabase", (res) => { | ||||
|                 if (res.ok) { | ||||
|                     this.loadDatabaseSize(); | ||||
|                     toast.success("Done"); | ||||
|                 } else { | ||||
|                     debug(res); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         confirmClearStatistics() { | ||||
|             this.$refs.confirmClearStatistics.show(); | ||||
|         }, | ||||
|  | ||||
|         clearStatistics() { | ||||
|             this.$root.clearStatistics((res) => { | ||||
|                 if (res.ok) { | ||||
|                     this.$router.go(); | ||||
|                 } else { | ||||
|                     toast.error(res.msg); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style></style> | ||||
							
								
								
									
										46
									
								
								src/components/settings/Notifications.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/components/settings/Notifications.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <div class="notification-list my-4"> | ||||
|             <p v-if="$root.notificationList.length === 0"> | ||||
|                 {{ $t("Not available, please setup.") }} | ||||
|             </p> | ||||
|             <p v-else> | ||||
|                 {{ $t("notificationDescription") }} | ||||
|             </p> | ||||
|  | ||||
|             <ul class="list-group mb-3" style="border-radius: 1rem;"> | ||||
|                 <li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item"> | ||||
|                     {{ notification.name }}<br> | ||||
|                     <a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|  | ||||
|             <button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()"> | ||||
|                 {{ $t("Setup Notification") }} | ||||
|             </button> | ||||
|         </div> | ||||
|  | ||||
|         <NotificationDialog ref="notificationDialog" /> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import NotificationDialog from "../../components/NotificationDialog.vue"; | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         NotificationDialog | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "../../assets/vars.scss"; | ||||
|  | ||||
| .dark { | ||||
|     .list-group-item { | ||||
|         background-color: $dark-bg2; | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										323
									
								
								src/components/settings/Security.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								src/components/settings/Security.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,323 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <div v-if="settingsLoaded" class="my-4"> | ||||
|             <!-- Change Password --> | ||||
|             <template v-if="!settings.disableAuth"> | ||||
|                 <p> | ||||
|                     {{ $t("Current User") }}: <strong>{{ username }}</strong> | ||||
|                 </p> | ||||
|  | ||||
|                 <h5 class="my-4">{{ $t("Change Password") }}</h5> | ||||
|                 <form class="mb-3" @submit.prevent="savePassword"> | ||||
|                     <div class="mb-3"> | ||||
|                         <label for="current-password" class="form-label"> | ||||
|                             {{ $t("Current Password") }} | ||||
|                         </label> | ||||
|                         <input | ||||
|                             id="current-password" | ||||
|                             v-model="password.currentPassword" | ||||
|                             type="password" | ||||
|                             class="form-control" | ||||
|                             required | ||||
|                         /> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="mb-3"> | ||||
|                         <label for="new-password" class="form-label"> | ||||
|                             {{ $t("New Password") }} | ||||
|                         </label> | ||||
|                         <input | ||||
|                             id="new-password" | ||||
|                             v-model="password.newPassword" | ||||
|                             type="password" | ||||
|                             class="form-control" | ||||
|                             required | ||||
|                         /> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="mb-3"> | ||||
|                         <label for="repeat-new-password" class="form-label"> | ||||
|                             {{ $t("Repeat New Password") }} | ||||
|                         </label> | ||||
|                         <input | ||||
|                             id="repeat-new-password" | ||||
|                             v-model="password.repeatNewPassword" | ||||
|                             type="password" | ||||
|                             class="form-control" | ||||
|                             :class="{ 'is-invalid': invalidPassword }" | ||||
|                             required | ||||
|                         /> | ||||
|                         <div class="invalid-feedback"> | ||||
|                             {{ $t("passwordNotMatchMsg") }} | ||||
|                         </div> | ||||
|                     </div> | ||||
|  | ||||
|                     <div> | ||||
|                         <button class="btn btn-primary" type="submit"> | ||||
|                             {{ $t("Update Password") }} | ||||
|                         </button> | ||||
|                     </div> | ||||
|                 </form> | ||||
|             </template> | ||||
|  | ||||
|             <div v-if="!$parent.$parent.settings.disableAuth" class="mt-5 mb-3"> | ||||
|                 <h5 class="my-4"> | ||||
|                     {{ $t("Two Factor Authentication") }} | ||||
|                 </h5> | ||||
|                 <div class="mb-4"> | ||||
|                     <button | ||||
|                         class="btn btn-primary me-2" | ||||
|                         type="button" | ||||
|                         @click="$refs.TwoFADialog.show()" | ||||
|                     > | ||||
|                         {{ $t("2FA Settings") }} | ||||
|                     </button> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <div class="my-4"> | ||||
|                 <!-- Advanced --> | ||||
|                 <h5 class="my-4">{{ $t("Advanced") }}</h5> | ||||
|  | ||||
|                 <div class="mb-4"> | ||||
|                     <button v-if="settings.disableAuth" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button> | ||||
|                     <button v-if="! settings.disableAuth" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button> | ||||
|                     <button v-if="! settings.disableAuth" class="btn btn-danger me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <TwoFADialog ref="TwoFADialog" /> | ||||
|  | ||||
|         <Confirm ref="confirmDisableAuth" btn-style="btn-danger" :yes-text="$t('I understand, please disable')" :no-text="$t('Leave')" @yes="disableAuth"> | ||||
|             <template v-if="$i18n.locale === 'es-ES' "> | ||||
|                 <p>Seguro que deseas <strong>deshabilitar la autenticación</strong>?</p> | ||||
|                 <p>Es para <strong>quien implementa autenticación de terceros</strong> ante Uptime Kuma como por ejemplo Cloudflare Access.</p> | ||||
|                 <p>Por favor usar con cuidado.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'pt-BR' "> | ||||
|                 <p>Você tem certeza que deseja <strong>desativar a autenticação</strong>?</p> | ||||
|                 <p>Isso é para <strong>alguém que tem autenticação de terceiros</strong> na frente do 'UpTime Kuma' como o Cloudflare Access.</p> | ||||
|                 <p>Por favor, utilize isso com cautela.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'zh-HK' "> | ||||
|                 <p>你是否確認<strong>取消登入認証</strong>?</p> | ||||
|                 <p>這個功能是設計給已有<strong>第三方認証</strong>的用家,例如 Cloudflare Access。</p> | ||||
|                 <p>請小心使用。</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'zh-CN' "> | ||||
|                 <p>是否确定 <strong>取消登录验证</strong>?</p> | ||||
|                 <p>这是为 <strong>有第三方认证</strong> 的用户提供的功能,如 Cloudflare Access</p> | ||||
|                 <p>请谨慎使用!</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'zh-TW' "> | ||||
|                 <p>你是否要<strong>取消登入驗證</strong>?</p> | ||||
|                 <p>此功能是設計給已有<strong>第三方認證</strong>的使用者,例如 Cloudflare Access。</p> | ||||
|                 <p>請謹慎使用。</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'de-DE' "> | ||||
|                 <p>Bist du sicher das du die <strong>Authentifizierung deaktivieren</strong> möchtest?</p> | ||||
|                 <p>Es ist für <strong>jemanden der eine externe Authentifizierung</strong> vor Uptime Kuma geschaltet hat, wie z.B. Cloudflare Access.</p> | ||||
|                 <p>Bitte mit Vorsicht nutzen.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'sr' "> | ||||
|                 <p>Да ли сте сигурни да желите да <strong>искључите аутентификацију</strong>?</p> | ||||
|                 <p>То је за <strong>оне који имају додату аутентификацију</strong> испред Uptime Kuma као на пример Cloudflare Access.</p> | ||||
|                 <p>Молим Вас користите ово са пажњом.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'sr-latn' "> | ||||
|                 <p>Da li ste sigurni da želite da <strong>isključite autentifikaciju</strong>?</p> | ||||
|                 <p>To je za <strong>one koji imaju dodatu autentifikaciju</strong> ispred Uptime Kuma kao na primer Cloudflare Access.</p> | ||||
|                 <p>Molim Vas koristite ovo sa pažnjom.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-if="$i18n.locale === 'hr-HR' "> | ||||
|                 <p>Jeste li sigurni da želite <strong>isključiti autentikaciju</strong>?</p> | ||||
|                 <p>To je za <strong>korisnike koji imaju vanjsku autentikaciju stranice</strong> ispred Uptime Kume, poput usluge Cloudflare Access.</p> | ||||
|                 <p>Pažljivo koristite ovu opciju.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'tr-TR' "> | ||||
|                 <p><strong>Şifreli girişi devre dışı bırakmak istediğinizden</strong>emin misiniz?</p> | ||||
|                 <p>Bu, Uptime Kuma'nın önünde Cloudflare Access gibi <strong>üçüncü taraf yetkilendirmesi olan</strong> kişiler içindir.</p> | ||||
|                 <p>Lütfen dikkatli kullanın.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'ko-KR' "> | ||||
|                 <p>정말로 <strong>인증 기능을 끌까요</strong>?</p> | ||||
|                 <p>이 기능은 <strong>Cloudflare Access와 같은 서드파티 인증</strong>을 Uptime Kuma 앞에 둔 사용자를 위한 기능이에요.</p> | ||||
|                 <p>신중하게 사용하세요.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'pl' "> | ||||
|                 <p>Czy na pewno chcesz <strong>wyłączyć autoryzację</strong>?</p> | ||||
|                 <p>Jest przeznaczony dla <strong>kogoś, kto ma autoryzację zewnętrzną</strong> przed Uptime Kuma, taką jak Cloudflare Access.</p> | ||||
|                 <p>Proszę używać ostrożnie.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'et-EE' "> | ||||
|                 <p>Kas soovid <strong>lülitada autentimise välja</strong>?</p> | ||||
|                 <p>Kastuamiseks <strong>välise autentimispakkujaga</strong>, näiteks Cloudflare Access.</p> | ||||
|                 <p>Palun kasuta vastutustundlikult.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'it-IT' "> | ||||
|                 <p>Si è certi di voler <strong>disabilitare l'autenticazione</strong>?</p> | ||||
|                 <p>È per <strong>chi ha l'autenticazione gestita da terze parti</strong> messa davanti ad Uptime Kuma, ad esempio Cloudflare Access.</p> | ||||
|                 <p>Utilizzare con attenzione.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'id-ID' "> | ||||
|                 <p>Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>?</p> | ||||
|                 <p>Ini untuk <strong>mereka yang memiliki autentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare.</p> | ||||
|                 <p>Gunakan dengan hati-hati.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'ru-RU' "> | ||||
|                 <p>Вы уверены, что хотите <strong>отключить авторизацию</strong>?</p> | ||||
|                 <p>Это подходит для <strong>тех, у кого стоит другая авторизация</strong> перед открытием Uptime Kuma, например Cloudflare Access.</p> | ||||
|                 <p>Пожалуйста, используйте с осторожностью.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'fa' "> | ||||
|                 <p>آیا مطمئن هستید که میخواهید <strong>احراز هویت را غیر فعال کنید</strong>?</p> | ||||
|                 <p>این ویژگی برای کسانی است که <strong> لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کردهاند</strong>، مانند Cloudflare Access.</p> | ||||
|                 <p>لطفا از این امکان با دقت استفاده کنید.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'bg-BG' "> | ||||
|                 <p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p> | ||||
|                 <p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access.</p> | ||||
|                 <p>Моля, използвайте с повишено внимание.</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'hu' "> | ||||
|                 <p>Biztos benne, hogy <strong>kikapcsolja a hitelesítést</strong>?</p> | ||||
|                 <p>Akkor érdemes, ha <strong>van 3rd-party hitelesítés</strong> az Uptime Kuma-t megelőzően mint a Cloudflare Access.</p> | ||||
|                 <p>Használja megfontoltan!</p> | ||||
|             </template> | ||||
|  | ||||
|             <template v-else-if="$i18n.locale === 'nb-NO' "> | ||||
|                 <p>Er du sikker på at du vil <strong>deaktiver autentisering</strong>?</p> | ||||
|                 <p>Dette er for <strong>de som har tredjepartsautorisering</strong> foran Uptime Kuma, for eksempel Cloudflare Access.</p> | ||||
|                 <p>Vennligst vær forsiktig.</p> | ||||
|             </template> | ||||
|  | ||||
|             <!-- English (en) --> | ||||
|             <template v-else> | ||||
|                 <p>Are you sure want to <strong>disable auth</strong>?</p> | ||||
|                 <p>It is for <strong>someone who have 3rd-party auth</strong> in front of Uptime Kuma such as Cloudflare Access.</p> | ||||
|                 <p>Please use it carefully.</p> | ||||
|             </template> | ||||
|         </Confirm> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Confirm from "../../components/Confirm.vue"; | ||||
| import TwoFADialog from "../../components/TwoFADialog.vue"; | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         Confirm, | ||||
|         TwoFADialog | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|         return { | ||||
|             username: "", | ||||
|             invalidPassword: false, | ||||
|             password: { | ||||
|                 currentPassword: "", | ||||
|                 newPassword: "", | ||||
|                 repeatNewPassword: "", | ||||
|             } | ||||
|         }; | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|         settings() { | ||||
|             return this.$parent.$parent.settings; | ||||
|         }, | ||||
|         saveSettings() { | ||||
|             return this.$parent.$parent.saveSettings; | ||||
|         }, | ||||
|         settingsLoaded() { | ||||
|             return this.$parent.$parent.settingsLoaded; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     watch: { | ||||
|         "password.repeatNewPassword"() { | ||||
|             this.invalidPassword = false; | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     mounted() { | ||||
|         this.loadUsername(); | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|         savePassword() { | ||||
|             if (this.password.newPassword !== this.password.repeatNewPassword) { | ||||
|                 this.invalidPassword = true; | ||||
|             } else { | ||||
|                 this.$root | ||||
|                     .getSocket() | ||||
|                     .emit("changePassword", this.password, (res) => { | ||||
|                         this.$root.toastRes(res); | ||||
|                         if (res.ok) { | ||||
|                             this.password.currentPassword = ""; | ||||
|                             this.password.newPassword = ""; | ||||
|                             this.password.repeatNewPassword = ""; | ||||
|                         } | ||||
|                     }); | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         loadUsername() { | ||||
|             const jwtPayload = this.$root.getJWTPayload(); | ||||
|  | ||||
|             if (jwtPayload) { | ||||
|                 this.username = jwtPayload.username; | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         disableAuth() { | ||||
|             this.settings.disableAuth = true; | ||||
|             this.saveSettings(); | ||||
|         }, | ||||
|  | ||||
|         enableAuth() { | ||||
|             this.settings.disableAuth = false; | ||||
|             this.saveSettings(); | ||||
|             this.$root.storage().removeItem("token"); | ||||
|             location.reload(); | ||||
|         }, | ||||
|  | ||||
|         confirmDisableAuth() { | ||||
|             this.$refs.confirmDisableAuth.show(); | ||||
|         }, | ||||
|  | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "../../assets/vars.scss"; | ||||
|  | ||||
| h5:after { | ||||
|     content: ""; | ||||
|     display: block; | ||||
|     width: 50%; | ||||
|     padding-top: 8px; | ||||
|     border-bottom: 1px solid $dark-border-color; | ||||
| } | ||||
| </style> | ||||
| @@ -307,4 +307,5 @@ export default { | ||||
|     steamApiKeyDescription: "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key here: ", | ||||
|     "Current User": "Current User", | ||||
|     recent: "Recent", | ||||
|     shrinkDatabaseDescription: "Trigger database VACCUM for SQLite. If your database is created after 1.10.0, AUTO_VACCUM is already enabled and this action is not needed.", | ||||
| }; | ||||
|   | ||||
| @@ -188,8 +188,8 @@ main { | ||||
|  | ||||
| .dark { | ||||
|     header { | ||||
|         background-color: #161b22; | ||||
|         border-bottom-color: #161b22 !important; | ||||
|         background-color: $dark-header-bg; | ||||
|         border-bottom-color: $dark-header-bg !important; | ||||
|  | ||||
|         span { | ||||
|             color: #f0f6fc; | ||||
|   | ||||
| @@ -7,521 +7,90 @@ | ||||
|  | ||||
|             <div class="shadow-box"> | ||||
|                 <div class="row"> | ||||
|                     <div class="col-md-6"> | ||||
|                         <h2 class="mb-2">{{ $t("Appearance") }}</h2> | ||||
|  | ||||
|                         <div class="mb-3"> | ||||
|                             <label for="language" class="form-label">{{ $t("Language") }}</label> | ||||
|                             <select id="language" v-model="$i18n.locale" class="form-select"> | ||||
|                                 <option v-for="(lang, i) in $i18n.availableLocales" :key="`Lang${i}`" :value="lang"> | ||||
|                                     {{ $i18n.messages[lang].languageName }} | ||||
|                                 </option> | ||||
|                             </select> | ||||
|                     <div class="settings-menu"> | ||||
|                         <div v-for="(item, key) in subMenus" | ||||
|                              :key="key" | ||||
|                              class="menu-item" | ||||
|                              :class="{ active: currentSubMenu == key }" | ||||
|                              @click="currentSubMenu = key" | ||||
|                         > | ||||
|                             {{ item.title }} | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="mb-3"> | ||||
|                             <label for="timezone" class="form-label">{{ $t("Theme") }}</label> | ||||
|  | ||||
|                             <div> | ||||
|                                 <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> | ||||
|                                     <input id="btncheck1" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="light"> | ||||
|                                     <label class="btn btn-outline-primary" for="btncheck1">{{ $t("Light") }}</label> | ||||
|  | ||||
|                                     <input id="btncheck2" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="dark"> | ||||
|                                     <label class="btn btn-outline-primary" for="btncheck2">{{ $t("Dark") }}</label> | ||||
|  | ||||
|                                     <input id="btncheck3" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="auto"> | ||||
|                                     <label class="btn btn-outline-primary" for="btncheck3">{{ $t("Auto") }}</label> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="mb-3"> | ||||
|                             <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label> | ||||
|                             <div> | ||||
|                                 <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> | ||||
|                                     <input id="btncheck4" v-model="$root.userHeartbeatBar" type="radio" class="btn-check" name="heartbeatBarTheme" autocomplete="off" value="normal"> | ||||
|                                     <label class="btn btn-outline-primary" for="btncheck4">{{ $t("Normal") }}</label> | ||||
|  | ||||
|                                     <input id="btncheck5" v-model="$root.userHeartbeatBar" type="radio" class="btn-check" name="heartbeatBarTheme" autocomplete="off" value="bottom"> | ||||
|                                     <label class="btn btn-outline-primary" for="btncheck5">{{ $t("Bottom") }}</label> | ||||
|  | ||||
|                                     <input id="btncheck6" v-model="$root.userHeartbeatBar" type="radio" class="btn-check" name="heartbeatBarTheme" autocomplete="off" value="none"> | ||||
|                                     <label class="btn btn-outline-primary" for="btncheck6">{{ $t("None") }}</label> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|  | ||||
|                         <!-- General Settings --> | ||||
|                         <h2 class="mt-5 mb-2">{{ $t("General") }}</h2> | ||||
|  | ||||
|                         <form class="mb-3" @submit.prevent="saveGeneral"> | ||||
|                             <!-- Timezone --> | ||||
|                             <div class="mb-4"> | ||||
|                                 <label for="timezone" class="form-label">{{ $t("Timezone") }}</label> | ||||
|                                 <select id="timezone" v-model="$root.userTimezone" class="form-select"> | ||||
|                                     <option value="auto"> | ||||
|                                         {{ $t("Auto") }}: {{ guessTimezone }} | ||||
|                                     </option> | ||||
|                                     <option v-for="(timezone, index) in timezoneList" :key="index" :value="timezone.value"> | ||||
|                                         {{ timezone.name }} | ||||
|                                     </option> | ||||
|                                 </select> | ||||
|                             </div> | ||||
|  | ||||
|                             <!-- Search Engine --> | ||||
|                             <div class="mb-4"> | ||||
|                                 <label class="form-label">{{ $t("Search Engine Visibility") }}</label> | ||||
|  | ||||
|                                 <div class="form-check"> | ||||
|                                     <input id="searchEngineIndexYes" v-model="settings.searchEngineIndex" class="form-check-input" type="radio" name="flexRadioDefault" :value="true" required> | ||||
|                                     <label class="form-check-label" for="searchEngineIndexYes"> | ||||
|                                         {{ $t("Allow indexing") }} | ||||
|                                     </label> | ||||
|                                 </div> | ||||
|                                 <div class="form-check"> | ||||
|                                     <input id="searchEngineIndexNo" v-model="settings.searchEngineIndex" class="form-check-input" type="radio" name="flexRadioDefault" :value="false" required> | ||||
|                                     <label class="form-check-label" for="searchEngineIndexNo"> | ||||
|                                         {{ $t("Discourage search engines from indexing site") }} | ||||
|                                     </label> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|  | ||||
|                             <!-- Entry Page --> | ||||
|                             <div class="mb-4"> | ||||
|                                 <label class="form-label">{{ $t("Entry Page") }}</label> | ||||
|  | ||||
|                                 <div class="form-check"> | ||||
|                                     <input id="entryPageYes" v-model="settings.entryPage" class="form-check-input" type="radio" name="statusPage" value="dashboard" required> | ||||
|                                     <label class="form-check-label" for="entryPageYes"> | ||||
|                                         {{ $t("Dashboard") }} | ||||
|                                     </label> | ||||
|                                 </div> | ||||
|  | ||||
|                                 <div class="form-check"> | ||||
|                                     <input id="entryPageNo" v-model="settings.entryPage" class="form-check-input" type="radio" name="statusPage" value="statusPage" required> | ||||
|                                     <label class="form-check-label" for="entryPageNo"> | ||||
|                                         {{ $t("Status Page") }} | ||||
|                                     </label> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|  | ||||
|                             <!-- Primary Base URL --> | ||||
|                             <div class="mb-4"> | ||||
|                                 <label class="form-label" for="primaryBaseURL">{{ $t("Primary Base URL") }}</label> | ||||
|  | ||||
|                                 <div class="input-group mb-3"> | ||||
|                                     <input id="primaryBaseURL" v-model="settings.primaryBaseURL" class="form-control" name="primaryBaseURL" placeholder="https://" pattern="https?://.+"> | ||||
|                                     <button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL">{{ $t("Auto Get") }}</button> | ||||
|                                 </div> | ||||
|  | ||||
|                                 <div class="form-text"> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|  | ||||
|                             <!-- Steam API Key --> | ||||
|                             <div class="mb-4"> | ||||
|                                 <label class="form-label" for="steamAPIKey">{{ $t("Steam API Key") }}</label> | ||||
|                                 <HiddenInput id="steamAPIKey" v-model="settings.steamAPIKey" /> | ||||
|                                 <div class="form-text"> | ||||
|                                     {{ $t("steamApiKeyDescription") }}<a href="https://steamcommunity.com/dev" target="_blank">https://steamcommunity.com/dev</a> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|  | ||||
|                             <!-- Monitor History --> | ||||
|                             <div class="mb-4"> | ||||
|                                 <h4 class="mt-4">{{ $t("Monitor History") }}</h4> | ||||
|                                 <div class="mt-2"> | ||||
|                                     <label for="keepDataPeriodDays" class="form-label">{{ $t("clearDataOlderThan", [ settings.keepDataPeriodDays ]) }}</label> | ||||
|                                     <input id="keepDataPeriodDays" v-model="settings.keepDataPeriodDays" type="number" class="form-control" required min="1" step="1"> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|  | ||||
|                             <!-- Save Button --> | ||||
|                             <div> | ||||
|                                 <button class="btn btn-primary" type="submit"> | ||||
|                                     {{ $t("Save") }} | ||||
|                                 </button> | ||||
|                             </div> | ||||
|                         </form> | ||||
|  | ||||
|                         <template v-if="loaded"> | ||||
|                             <!-- Change Password --> | ||||
|                             <template v-if="! settings.disableAuth"> | ||||
|                                 <h2 class="mt-5 mb-2">{{ $t("Change Password") }}</h2> | ||||
|                                 <p>{{ $t("Current User") }}: <strong>{{ username }}</strong></p> | ||||
|                                 <form class="mb-3" @submit.prevent="savePassword"> | ||||
|                                     <div class="mb-3"> | ||||
|                                         <label for="current-password" class="form-label">{{ $t("Current Password") }}</label> | ||||
|                                         <input id="current-password" v-model="password.currentPassword" type="password" class="form-control" required> | ||||
|                                     </div> | ||||
|  | ||||
|                                     <div class="mb-3"> | ||||
|                                         <label for="new-password" class="form-label">{{ $t("New Password") }}</label> | ||||
|                                         <input id="new-password" v-model="password.newPassword" type="password" class="form-control" required> | ||||
|                                     </div> | ||||
|  | ||||
|                                     <div class="mb-3"> | ||||
|                                         <label for="repeat-new-password" class="form-label">{{ $t("Repeat New Password") }}</label> | ||||
|                                         <input id="repeat-new-password" v-model="password.repeatNewPassword" type="password" class="form-control" :class="{ 'is-invalid' : invalidPassword }" required> | ||||
|                                         <div class="invalid-feedback"> | ||||
|                                             {{ $t("passwordNotMatchMsg") }} | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|  | ||||
|                                     <div> | ||||
|                                         <button class="btn btn-primary" type="submit"> | ||||
|                                             {{ $t("Update Password") }} | ||||
|                                         </button> | ||||
|                                     </div> | ||||
|                                 </form> | ||||
|                             </template> | ||||
|  | ||||
|                             <div v-if="! settings.disableAuth" class="mt-5 mb-3"> | ||||
|                                 <h2 class="mb-2"> | ||||
|                                     {{ $t("Two Factor Authentication") }} | ||||
|                                 </h2> | ||||
|                                 <button class="btn btn-primary me-2" type="button" @click="$refs.TwoFADialog.show()">{{ $t("2FA Settings") }}</button> | ||||
|                             </div> | ||||
|  | ||||
|                             <h2 class="mt-5 mb-2">{{ $t("Export Backup") }}</h2> | ||||
|  | ||||
|                             <p> | ||||
|                                 {{ $t("backupDescription") }} <br /> | ||||
|                                 ({{ $t("backupDescription2") }}) <br /> | ||||
|                             </p> | ||||
|  | ||||
|                             <div class="mb-2"> | ||||
|                                 <button class="btn btn-primary" @click="downloadBackup">{{ $t("Export") }}</button> | ||||
|                             </div> | ||||
|  | ||||
|                             <p><strong>{{ $t("backupDescription3") }}</strong></p> | ||||
|  | ||||
|                             <h2 class="mt-5 mb-2">{{ $t("Import Backup") }}</h2> | ||||
|  | ||||
|                             <label class="form-label">{{ $t("Options") }}:</label> | ||||
|                             <br> | ||||
|                             <div class="form-check form-check-inline"> | ||||
|                                 <input id="radioKeep" v-model="importHandle" class="form-check-input" type="radio" name="radioImportHandle" value="keep"> | ||||
|                                 <label class="form-check-label" for="radioKeep">{{ $t("Keep both") }}</label> | ||||
|                             </div> | ||||
|                             <div class="form-check form-check-inline"> | ||||
|                                 <input id="radioSkip" v-model="importHandle" class="form-check-input" type="radio" name="radioImportHandle" value="skip"> | ||||
|                                 <label class="form-check-label" for="radioSkip">{{ $t("Skip existing") }}</label> | ||||
|                             </div> | ||||
|                             <div class="form-check form-check-inline"> | ||||
|                                 <input id="radioOverwrite" v-model="importHandle" class="form-check-input" type="radio" name="radioImportHandle" value="overwrite"> | ||||
|                                 <label class="form-check-label" for="radioOverwrite">{{ $t("Overwrite") }}</label> | ||||
|                             </div> | ||||
|                             <div class="form-text mb-2"> | ||||
|                                 {{ $t("importHandleDescription") }} | ||||
|                             </div> | ||||
|  | ||||
|                             <div class="mb-2"> | ||||
|                                 <input id="importBackup" type="file" class="form-control" accept="application/json"> | ||||
|                             </div> | ||||
|  | ||||
|                             <div class="input-group mb-2 justify-content-end"> | ||||
|                                 <button type="button" class="btn btn-outline-primary" :disabled="processing" @click="confirmImport"> | ||||
|                                     <div v-if="processing" class="spinner-border spinner-border-sm me-1"></div> | ||||
|                                     {{ $t("Import") }} | ||||
|                                 </button> | ||||
|                             </div> | ||||
|  | ||||
|                             <div v-if="importAlert" class="alert alert-danger mt-3" style="padding: 6px 16px;"> | ||||
|                                 {{ importAlert }} | ||||
|                             </div> | ||||
|  | ||||
|                             <!-- Advanced --> | ||||
|                             <h2 class="mt-5 mb-2">{{ $t("Advanced") }}</h2> | ||||
|  | ||||
|                             <div class="mb-3"> | ||||
|                                 <button v-if="settings.disableAuth" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button> | ||||
|                                 <button v-if="! settings.disableAuth" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button> | ||||
|                                 <button v-if="! settings.disableAuth" class="btn btn-danger me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button> | ||||
|                                 <button class="btn btn-outline-danger me-2 mb-2" @click="confirmClearStatistics">{{ $t("Clear all statistics") }}</button> | ||||
|                                 <button class="btn btn-info me-2 mb-2" @click="shrinkDatabase">{{ $t("Shrink Database") }} ({{ databaseSizeDisplay }})</button> | ||||
|                             </div> | ||||
|                         </template> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="col-md-6"> | ||||
|                         <div v-if="$root.isMobile" class="mt-3" /> | ||||
|  | ||||
|                         <!-- Notifications --> | ||||
|                         <div class="notification-list "> | ||||
|                             <h2>{{ $t("Notifications") }}</h2> | ||||
|                             <p v-if="$root.notificationList.length === 0"> | ||||
|                                 {{ $t("Not available, please setup.") }} | ||||
|                             </p> | ||||
|                             <p v-else> | ||||
|                                 {{ $t("notificationDescription") }} | ||||
|                             </p> | ||||
|  | ||||
|                             <ul class="list-group mb-3" style="border-radius: 1rem;"> | ||||
|                                 <li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item"> | ||||
|                                     {{ notification.name }}<br> | ||||
|                                     <a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a> | ||||
|                                 </li> | ||||
|                             </ul> | ||||
|  | ||||
|                             <button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()"> | ||||
|                                 {{ $t("Setup Notification") }} | ||||
|                             </button> | ||||
|                     <div class="settings-content"> | ||||
|                         <div class="settings-content-header"> | ||||
|                             {{ subMenus[currentSubMenu].title }} | ||||
|                         </div> | ||||
|                         <div class="mx-3"> | ||||
|                             <component :is="subMenus[currentSubMenu].component" /> | ||||
|                         </div> | ||||
|  | ||||
|                         <!-- Info --> | ||||
|                         <h2 class="mt-5">{{ $t("Info") }}</h2> | ||||
|  | ||||
|                         {{ $t("Version") }}: {{ $root.info.version }} <br /> | ||||
|                         <a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">{{ $t("Check Update On GitHub") }}</a> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <NotificationDialog ref="notificationDialog" /> | ||||
|             <TwoFADialog ref="TwoFADialog" /> | ||||
|  | ||||
|             <Confirm ref="confirmDisableAuth" btn-style="btn-danger" :yes-text="$t('I understand, please disable')" :no-text="$t('Leave')" @yes="disableAuth"> | ||||
|                 <template v-if="$i18n.locale === 'es-ES' "> | ||||
|                     <p>Seguro que deseas <strong>deshabilitar la autenticación</strong>?</p> | ||||
|                     <p>Es para <strong>quien implementa autenticación de terceros</strong> ante Uptime Kuma como por ejemplo Cloudflare Access.</p> | ||||
|                     <p>Por favor usar con cuidado.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'pt-BR' "> | ||||
|                     <p>Você tem certeza que deseja <strong>desativar a autenticação</strong>?</p> | ||||
|                     <p>Isso é para <strong>alguém que tem autenticação de terceiros</strong> na frente do 'UpTime Kuma' como o Cloudflare Access.</p> | ||||
|                     <p>Por favor, utilize isso com cautela.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'zh-HK' "> | ||||
|                     <p>你是否確認<strong>取消登入認証</strong>?</p> | ||||
|                     <p>這個功能是設計給已有<strong>第三方認証</strong>的用家,例如 Cloudflare Access。</p> | ||||
|                     <p>請小心使用。</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'zh-CN' "> | ||||
|                     <p>是否确定 <strong>取消登录验证</strong>?</p> | ||||
|                     <p>这是为 <strong>有第三方认证</strong> 的用户提供的功能,如 Cloudflare Access</p> | ||||
|                     <p>请谨慎使用!</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'zh-TW' "> | ||||
|                     <p>你是否要<strong>取消登入驗證</strong>?</p> | ||||
|                     <p>此功能是設計給已有<strong>第三方認證</strong>的使用者,例如 Cloudflare Access。</p> | ||||
|                     <p>請謹慎使用。</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'de-DE' "> | ||||
|                     <p>Bist du sicher das du die <strong>Authentifizierung deaktivieren</strong> möchtest?</p> | ||||
|                     <p>Es ist für <strong>jemanden der eine externe Authentifizierung</strong> vor Uptime Kuma geschaltet hat, wie z.B. Cloudflare Access.</p> | ||||
|                     <p>Bitte mit Vorsicht nutzen.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'sr' "> | ||||
|                     <p>Да ли сте сигурни да желите да <strong>искључите аутентификацију</strong>?</p> | ||||
|                     <p>То је за <strong>оне који имају додату аутентификацију</strong> испред Uptime Kuma као на пример Cloudflare Access.</p> | ||||
|                     <p>Молим Вас користите ово са пажњом.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'sr-latn' "> | ||||
|                     <p>Da li ste sigurni da želite da <strong>isključite autentifikaciju</strong>?</p> | ||||
|                     <p>To je za <strong>one koji imaju dodatu autentifikaciju</strong> ispred Uptime Kuma kao na primer Cloudflare Access.</p> | ||||
|                     <p>Molim Vas koristite ovo sa pažnjom.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-if="$i18n.locale === 'hr-HR' "> | ||||
|                     <p>Jeste li sigurni da želite <strong>isključiti autentikaciju</strong>?</p> | ||||
|                     <p>To je za <strong>korisnike koji imaju vanjsku autentikaciju stranice</strong> ispred Uptime Kume, poput usluge Cloudflare Access.</p> | ||||
|                     <p>Pažljivo koristite ovu opciju.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'tr-TR' "> | ||||
|                     <p><strong>Şifreli girişi devre dışı bırakmak istediğinizden</strong>emin misiniz?</p> | ||||
|                     <p>Bu, Uptime Kuma'nın önünde Cloudflare Access gibi <strong>üçüncü taraf yetkilendirmesi olan</strong> kişiler içindir.</p> | ||||
|                     <p>Lütfen dikkatli kullanın.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'ko-KR' "> | ||||
|                     <p>정말로 <strong>인증 기능을 끌까요</strong>?</p> | ||||
|                     <p>이 기능은 <strong>Cloudflare Access와 같은 서드파티 인증</strong>을 Uptime Kuma 앞에 둔 사용자를 위한 기능이에요.</p> | ||||
|                     <p>신중하게 사용하세요.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'pl' "> | ||||
|                     <p>Czy na pewno chcesz <strong>wyłączyć autoryzację</strong>?</p> | ||||
|                     <p>Jest przeznaczony dla <strong>kogoś, kto ma autoryzację zewnętrzną</strong> przed Uptime Kuma, taką jak Cloudflare Access.</p> | ||||
|                     <p>Proszę używać ostrożnie.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'et-EE' "> | ||||
|                     <p>Kas soovid <strong>lülitada autentimise välja</strong>?</p> | ||||
|                     <p>Kastuamiseks <strong>välise autentimispakkujaga</strong>, näiteks Cloudflare Access.</p> | ||||
|                     <p>Palun kasuta vastutustundlikult.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'it-IT' "> | ||||
|                     <p>Si è certi di voler <strong>disabilitare l'autenticazione</strong>?</p> | ||||
|                     <p>È per <strong>chi ha l'autenticazione gestita da terze parti</strong> messa davanti ad Uptime Kuma, ad esempio Cloudflare Access.</p> | ||||
|                     <p>Utilizzare con attenzione.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'id-ID' "> | ||||
|                     <p>Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>?</p> | ||||
|                     <p>Ini untuk <strong>mereka yang memiliki autentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare.</p> | ||||
|                     <p>Gunakan dengan hati-hati.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'ru-RU' "> | ||||
|                     <p>Вы уверены, что хотите <strong>отключить авторизацию</strong>?</p> | ||||
|                     <p>Это подходит для <strong>тех, у кого стоит другая авторизация</strong> перед открытием Uptime Kuma, например Cloudflare Access.</p> | ||||
|                     <p>Пожалуйста, используйте с осторожностью.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'fa' "> | ||||
|                     <p>آیا مطمئن هستید که میخواهید <strong>احراز هویت را غیر فعال کنید</strong>?</p> | ||||
|                     <p>این ویژگی برای کسانی است که <strong> لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کردهاند</strong>، مانند Cloudflare Access.</p> | ||||
|                     <p>لطفا از این امکان با دقت استفاده کنید.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'bg-BG' "> | ||||
|                     <p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p> | ||||
|                     <p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access.</p> | ||||
|                     <p>Моля, използвайте с повишено внимание.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'hu' "> | ||||
|                     <p>Biztos benne, hogy <strong>kikapcsolja a hitelesítést</strong>?</p> | ||||
|                     <p>Akkor érdemes, ha <strong>van 3rd-party hitelesítés</strong> az Uptime Kuma-t megelőzően mint a Cloudflare Access.</p> | ||||
|                     <p>Használja megfontoltan!</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <template v-else-if="$i18n.locale === 'nb-NO' "> | ||||
|                     <p>Er du sikker på at du vil <strong>deaktiver autentisering</strong>?</p> | ||||
|                     <p>Dette er for <strong>de som har tredjepartsautorisering</strong> foran Uptime Kuma, for eksempel Cloudflare Access.</p> | ||||
|                     <p>Vennligst vær forsiktig.</p> | ||||
|                 </template> | ||||
|  | ||||
|                 <!-- English (en) --> | ||||
|                 <template v-else> | ||||
|                     <p>Are you sure want to <strong>disable auth</strong>?</p> | ||||
|                     <p>It is for <strong>someone who have 3rd-party auth</strong> in front of Uptime Kuma such as Cloudflare Access.</p> | ||||
|                     <p>Please use it carefully.</p> | ||||
|                 </template> | ||||
|             </Confirm> | ||||
|  | ||||
|             <Confirm ref="confirmClearStatistics" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="clearStatistics"> | ||||
|                 {{ $t("confirmClearStatisticsMsg") }} | ||||
|             </Confirm> | ||||
|             <Confirm ref="confirmImport" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="importBackup"> | ||||
|                 {{ $t("confirmImportMsg") }} | ||||
|             </Confirm> | ||||
|         </div> | ||||
|     </transition> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import HiddenInput from "../components/HiddenInput.vue"; | ||||
| import Confirm from "../components/Confirm.vue"; | ||||
| import dayjs from "dayjs"; | ||||
| import utc from "dayjs/plugin/utc"; | ||||
| import timezone from "dayjs/plugin/timezone"; | ||||
| import NotificationDialog from "../components/NotificationDialog.vue"; | ||||
| import TwoFADialog from "../components/TwoFADialog.vue"; | ||||
| import jwt_decode from "jwt-decode"; | ||||
| dayjs.extend(utc); | ||||
| dayjs.extend(timezone); | ||||
| import { markRaw } from "vue"; | ||||
|  | ||||
| import { timezoneList, setPageLocale } from "../util-frontend"; | ||||
| import { useToast } from "vue-toastification"; | ||||
| import { debug } from "../util.ts"; | ||||
|  | ||||
| const toast = useToast(); | ||||
| import Appearance from "../components/settings/Appearance.vue"; | ||||
| import General from "../components/settings/General.vue"; | ||||
| import Notifications from "../components/settings/Notifications.vue"; | ||||
| import MonitorHistory from "../components/settings/MonitorHistory.vue"; | ||||
| import Security from "../components/settings/Security.vue"; | ||||
| import Backup from "../components/settings/Backup.vue"; | ||||
| import About from "../components/settings/About.vue"; | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         NotificationDialog, | ||||
|         TwoFADialog, | ||||
|         Confirm, | ||||
|         HiddenInput, | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|         return { | ||||
|             timezoneList: timezoneList(), | ||||
|             guessTimezone: dayjs.tz.guess(), | ||||
|             show: true, | ||||
|             invalidPassword: false, | ||||
|             password: { | ||||
|                 currentPassword: "", | ||||
|                 newPassword: "", | ||||
|                 repeatNewPassword: "", | ||||
|             }, | ||||
|             settings: { | ||||
|  | ||||
|             show: true, | ||||
|  | ||||
|             settings: {}, | ||||
|             settingsLoaded: false, | ||||
|  | ||||
|             subMenus: { | ||||
|                 general: { | ||||
|                     title: this.$t("General"), | ||||
|                     component: markRaw(General), | ||||
|                 }, | ||||
|                 appearance: { | ||||
|                     title: this.$t("Appearance"), | ||||
|                     component: markRaw(Appearance), | ||||
|                 }, | ||||
|                 notifications: { | ||||
|                     title: this.$t("Notifications"), | ||||
|                     component: markRaw(Notifications), | ||||
|                 }, | ||||
|                 monitorHistory: { | ||||
|                     title: this.$t("Monitor History"), | ||||
|                     component: markRaw(MonitorHistory), | ||||
|                 }, | ||||
|                 security: { | ||||
|                     title: this.$t("Security"), | ||||
|                     component: markRaw(Security), | ||||
|                 }, | ||||
|                 backup: { | ||||
|                     title: this.$t("Backup"), | ||||
|                     component: markRaw(Backup), | ||||
|                 }, | ||||
|                 about: { | ||||
|                     title: this.$t("About"), | ||||
|                     component: markRaw(About), | ||||
|                 } | ||||
|             }, | ||||
|             loaded: false, | ||||
|             importAlert: null, | ||||
|             importHandle: "skip", | ||||
|             processing: false, | ||||
|             databaseSize: 0, | ||||
|             currentSubMenu: "general", | ||||
|         }; | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|         databaseSizeDisplay() { | ||||
|             return Math.round(this.databaseSize / 1024 / 1024 * 10) / 10 + " MB"; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     watch: { | ||||
|         "password.repeatNewPassword"() { | ||||
|             this.invalidPassword = false; | ||||
|         }, | ||||
|  | ||||
|         "$i18n.locale"() { | ||||
|             localStorage.locale = this.$i18n.locale; | ||||
|             setPageLocale(); | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     mounted() { | ||||
|         this.loadUsername(); | ||||
|         this.loadSettings(); | ||||
|         this.loadDatabaseSize(); | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|  | ||||
|         saveGeneral() { | ||||
|             localStorage.timezone = this.$root.userTimezone; | ||||
|             this.saveSettings(); | ||||
|         }, | ||||
|  | ||||
|         savePassword() { | ||||
|             if (this.password.newPassword !== this.password.repeatNewPassword) { | ||||
|                 this.invalidPassword = true; | ||||
|             } else { | ||||
|                 this.$root.getSocket().emit("changePassword", this.password, (res) => { | ||||
|                     this.$root.toastRes(res); | ||||
|                     if (res.ok) { | ||||
|                         this.password.currentPassword = ""; | ||||
|                         this.password.newPassword = ""; | ||||
|                         this.password.repeatNewPassword = ""; | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         loadUsername() { | ||||
|             const jwtPayload = this.$root.getJWTPayload(); | ||||
|  | ||||
|             if (jwtPayload) { | ||||
|                 this.username = jwtPayload.username; | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         loadSettings() { | ||||
|             this.$root.getSocket().emit("getSettings", (res) => { | ||||
|                 this.settings = res.data; | ||||
| @@ -538,7 +107,7 @@ export default { | ||||
|                     this.settings.keepDataPeriodDays = 180; | ||||
|                 } | ||||
|  | ||||
|                 this.loaded = true; | ||||
|                 this.settingsLoaded = true; | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
| @@ -548,116 +117,6 @@ export default { | ||||
|                 this.loadSettings(); | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         confirmDisableAuth() { | ||||
|             this.$refs.confirmDisableAuth.show(); | ||||
|         }, | ||||
|  | ||||
|         confirmClearStatistics() { | ||||
|             this.$refs.confirmClearStatistics.show(); | ||||
|         }, | ||||
|  | ||||
|         confirmImport() { | ||||
|             this.$refs.confirmImport.show(); | ||||
|         }, | ||||
|  | ||||
|         disableAuth() { | ||||
|             this.settings.disableAuth = true; | ||||
|             this.saveSettings(); | ||||
|         }, | ||||
|  | ||||
|         enableAuth() { | ||||
|             this.settings.disableAuth = false; | ||||
|             this.saveSettings(); | ||||
|             this.$root.storage().removeItem("token"); | ||||
|             location.reload(); | ||||
|         }, | ||||
|  | ||||
|         downloadBackup() { | ||||
|             let time = dayjs().format("YYYY_MM_DD-hh_mm_ss"); | ||||
|             let fileName = `Uptime_Kuma_Backup_${time}.json`; | ||||
|             let monitorList = Object.values(this.$root.monitorList); | ||||
|             let exportData = { | ||||
|                 version: this.$root.info.version, | ||||
|                 notificationList: this.$root.notificationList, | ||||
|                 monitorList: monitorList, | ||||
|             }; | ||||
|             exportData = JSON.stringify(exportData, null, 4); | ||||
|             let downloadItem = document.createElement("a"); | ||||
|             downloadItem.setAttribute("href", "data:application/json;charset=utf-8," + encodeURIComponent(exportData)); | ||||
|             downloadItem.setAttribute("download", fileName); | ||||
|             downloadItem.click(); | ||||
|         }, | ||||
|  | ||||
|         importBackup() { | ||||
|             this.processing = true; | ||||
|             let uploadItem = document.getElementById("importBackup").files; | ||||
|  | ||||
|             if (uploadItem.length <= 0) { | ||||
|                 this.processing = false; | ||||
|                 return this.importAlert = this.$t("alertNoFile"); | ||||
|             } | ||||
|  | ||||
|             if (uploadItem.item(0).type !== "application/json") { | ||||
|                 this.processing = false; | ||||
|                 return this.importAlert = this.$t("alertWrongFileType"); | ||||
|             } | ||||
|  | ||||
|             let fileReader = new FileReader(); | ||||
|             fileReader.readAsText(uploadItem.item(0)); | ||||
|  | ||||
|             fileReader.onload = item => { | ||||
|                 this.$root.uploadBackup(item.target.result, this.importHandle, (res) => { | ||||
|                     this.processing = false; | ||||
|  | ||||
|                     if (res.ok) { | ||||
|                         toast.success(res.msg); | ||||
|                     } else { | ||||
|                         toast.error(res.msg); | ||||
|                     } | ||||
|                 }); | ||||
|             }; | ||||
|         }, | ||||
|  | ||||
|         clearStatistics() { | ||||
|             this.$root.clearStatistics((res) => { | ||||
|                 if (res.ok) { | ||||
|                     this.$router.go(); | ||||
|                 } else { | ||||
|                     toast.error(res.msg); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         autoGetPrimaryBaseURL() { | ||||
|             this.settings.primaryBaseURL = location.protocol + "//" + location.host; | ||||
|         }, | ||||
|  | ||||
|         shrinkDatabase() { | ||||
|             this.$root.getSocket().emit("shrinkDatabase", (res) => { | ||||
|                 if (res.ok) { | ||||
|                     this.loadDatabaseSize(); | ||||
|                     toast.success("Done"); | ||||
|                 } else { | ||||
|                     debug(res); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         loadDatabaseSize() { | ||||
|             debug("load database size"); | ||||
|             this.$root.getSocket().emit("getDatabaseSize", (res) => { | ||||
|  | ||||
|                 if (res.ok) { | ||||
|                     this.databaseSize = res.size; | ||||
|                     debug("database size: " + res.size); | ||||
|                 } else { | ||||
|                     debug(res); | ||||
|                 } | ||||
|  | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
| @@ -667,37 +126,8 @@ export default { | ||||
|  | ||||
| .shadow-box { | ||||
|     padding: 20px; | ||||
| } | ||||
|  | ||||
| .btn-check:active + .btn-outline-primary, | ||||
| .btn-check:checked + .btn-outline-primary, | ||||
| .btn-check:hover + .btn-outline-primary { | ||||
|     color: #fff; | ||||
| } | ||||
|  | ||||
| .dark { | ||||
|     .list-group-item { | ||||
|         background-color: $dark-bg2; | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
|  | ||||
|     .btn-check:active + .btn-outline-primary, | ||||
|     .btn-check:checked + .btn-outline-primary, | ||||
|     .btn-check:hover + .btn-outline-primary { | ||||
|         color: #000; | ||||
|     } | ||||
|  | ||||
|     #importBackup { | ||||
|         &::file-selector-button { | ||||
|             color: $primary; | ||||
|             background-color: $dark-bg; | ||||
|         } | ||||
|  | ||||
|         &:hover:not(:disabled):not([readonly])::file-selector-button { | ||||
|             color: $dark-font-color2; | ||||
|             background-color: $primary; | ||||
|         } | ||||
|     } | ||||
|     min-height: calc(100vh - 155px); | ||||
|     max-height: calc(100vh - 30px); | ||||
| } | ||||
|  | ||||
| footer { | ||||
| @@ -707,4 +137,55 @@ footer { | ||||
|     padding-bottom: 30px; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .settings-menu { | ||||
|     flex: 0 0 auto; | ||||
|     width: 300px; | ||||
|  | ||||
|     .menu-item { | ||||
|         border-radius: 10px; | ||||
|         margin: 0.5em; | ||||
|         padding: 0.7em 1em; | ||||
|         cursor: pointer; | ||||
|     } | ||||
|  | ||||
|     .menu-item:hover { | ||||
|         background: $highlight-white; | ||||
|  | ||||
|         .dark & { | ||||
|             background: $dark-header-bg; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .menu-item.active { | ||||
|         background: $highlight-white; | ||||
|         border-left: 4px solid $primary; | ||||
|         border-top-left-radius: 0; | ||||
|         border-bottom-left-radius: 0; | ||||
|  | ||||
|         .dark & { | ||||
|             background: $dark-header-bg; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .settings-content { | ||||
|     flex: 0 0 auto; | ||||
|     width: calc(100% - 300px); | ||||
|  | ||||
|     .settings-content-header { | ||||
|         width: calc(100% + 20px); | ||||
|         border-bottom: 1px solid #dee2e6; | ||||
|         border-radius: 0 10px 0 0; | ||||
|         margin-top: -20px; | ||||
|         margin-right: -20px; | ||||
|         padding: 12.5px 1em; | ||||
|         font-size: 26px; | ||||
|  | ||||
|         .dark & { | ||||
|             background: $dark-header-bg; | ||||
|             border-bottom: 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </style> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user