Compare commits
	
		
			22 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8403ac0e93 | ||
|  | c2f21b70dd | ||
|  | 520145e0e3 | ||
|  | 6a132187a2 | ||
|  | a63a9ccd76 | ||
|  | ff1eb791db | ||
|  | 13bd88b979 | ||
|  | 5b0c244920 | ||
|  | 0318a17cac | ||
|  | b7a91563b0 | ||
|  | 75296ed8ee | ||
|  | 09bee45b2f | ||
|  | 3573c48872 | ||
|  | 784841c221 | ||
|  | ed788a1861 | ||
|  | ab19afca16 | ||
|  | bd6b08505a | ||
|  | acd64f25f2 | ||
|  | 087be2c232 | ||
|  | 91a3272843 | ||
|  | 6e64f0a11b | ||
|  | 8f34f76a1d | 
							
								
								
									
										2
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							| @@ -5,7 +5,7 @@ | ||||
|     "esbenp.prettier-vscode", | ||||
|     "editorconfig.editorconfig", | ||||
|     "vue.volar", | ||||
|     "wayou.vscode-todo-highlight", | ||||
|     "wayou.vscode-todo-highlight" | ||||
|   ], | ||||
|   "unwantedRecommendations": [ | ||||
|     "octref.vetur", | ||||
|   | ||||
							
								
								
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|   "editor.formatOnSave": true, | ||||
|   "[vue][javascript][typescript][javascriptreact]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode", | ||||
|     "editor.codeActionsOnSave": ["source.fixAll.eslint"], | ||||
|     "editor.codeActionsOnSave": ["source.fixAll.eslint"] | ||||
|   }, | ||||
|   "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], | ||||
|   "typescript.tsdk": "node_modules/typescript/lib", | ||||
| @@ -15,7 +15,7 @@ | ||||
|       "**/node_modules/": true, | ||||
|       "/node_modules/**": true, | ||||
|       "**/env/": true, | ||||
|       "/env/**": true, | ||||
|       "/env/**": true | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										36
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								index.html
									
									
									
									
									
								
							| @@ -1,24 +1,22 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|   <head> | ||||
|     <title><%= productName %></title> | ||||
|  | ||||
| <head> | ||||
|   <title> | ||||
|     <%= productName %> | ||||
|   </title> | ||||
|  | ||||
|   <meta charset="utf-8" /> | ||||
|   <meta name="robots" content="noindex" /> | ||||
|   <meta name="description" content="<%= productDescription %>" /> | ||||
|   <meta name="format-detection" content="telephone=no" /> | ||||
|   <meta name="msapplication-tap-highlight" content="no" /> | ||||
|   <meta name="viewport" | ||||
|     content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>" /> | ||||
|   <link rel="icon" type="image/ico" href="favicon.ico" /> | ||||
|   <script src="/env-config.js"></script> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|   <!-- quasar:entry-point --> | ||||
| </body> | ||||
|     <meta charset="utf-8" /> | ||||
|     <meta name="robots" content="noindex" /> | ||||
|     <meta name="description" content="<%= productDescription %>" /> | ||||
|     <meta name="format-detection" content="telephone=no" /> | ||||
|     <meta name="msapplication-tap-highlight" content="no" /> | ||||
|     <meta | ||||
|       name="viewport" | ||||
|       content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>" | ||||
|     /> | ||||
|     <link rel="icon" type="image/ico" href="favicon.ico" /> | ||||
|     <script src="/env-config.js"></script> | ||||
|   </head> | ||||
|  | ||||
|   <body> | ||||
|     <!-- quasar:entry-point --> | ||||
|   </body> | ||||
| </html> | ||||
|   | ||||
							
								
								
									
										6055
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6055
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										36
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "web", | ||||
|   "version": "0.101.9", | ||||
|   "version": "0.101.16", | ||||
|   "private": true, | ||||
|   "productName": "Tactical RMM", | ||||
|   "scripts": { | ||||
| @@ -10,13 +10,13 @@ | ||||
|     "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@quasar/extras": "1.15.6", | ||||
|     "apexcharts": "3.35.5", | ||||
|     "axios": "0.27.2", | ||||
|     "@quasar/extras": "1.15.11", | ||||
|     "apexcharts": "3.37.1", | ||||
|     "axios": "1.3.4", | ||||
|     "dotenv": "16.0.3", | ||||
|     "qrcode.vue": "3.3.3", | ||||
|     "quasar": "2.10.2", | ||||
|     "vue": "3.2.45", | ||||
|     "qrcode.vue": "3.3.4", | ||||
|     "quasar": "2.11.8", | ||||
|     "vue": "3.2.47", | ||||
|     "vue3-ace-editor": "2.2.2", | ||||
|     "vue3-apexcharts": "1.4.1", | ||||
|     "vuedraggable": "4.1.0", | ||||
| @@ -24,17 +24,17 @@ | ||||
|     "vuex": "4.1.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@quasar/cli": "^1.3.2", | ||||
|     "@intlify/vite-plugin-vue-i18n": "^6.0.3", | ||||
|     "@quasar/app-vite": "^1.1.3", | ||||
|     "@types/node": "^18.11.10", | ||||
|     "@typescript-eslint/eslint-plugin": "^5.45.0", | ||||
|     "@typescript-eslint/parser": "^5.45.0", | ||||
|     "autoprefixer": "10.4.13", | ||||
|     "eslint": "8.28.0", | ||||
|     "eslint-config-prettier": "8.5.0", | ||||
|     "@quasar/cli": "^2.0.0", | ||||
|     "@intlify/unplugin-vue-i18n": "^0.9.2", | ||||
|     "@quasar/app-vite": "^1.2.1", | ||||
|     "@types/node": "^18.15.3", | ||||
|     "@typescript-eslint/eslint-plugin": "^5.55.0", | ||||
|     "@typescript-eslint/parser": "^5.55.0", | ||||
|     "autoprefixer": "10.4.14", | ||||
|     "eslint": "8.36.0", | ||||
|     "eslint-config-prettier": "8.7.0", | ||||
|     "eslint-plugin-vue": "8.7.1", | ||||
|     "prettier": "2.8.0", | ||||
|     "typescript": "4.9.3" | ||||
|     "prettier": "2.8.4", | ||||
|     "typescript": "4.9.5" | ||||
|   } | ||||
| } | ||||
| @@ -4,18 +4,18 @@ | ||||
| module.exports = { | ||||
|   plugins: [ | ||||
|     // https://github.com/postcss/autoprefixer | ||||
|     require('autoprefixer')({ | ||||
|     require("autoprefixer")({ | ||||
|       overrideBrowserslist: [ | ||||
|         'last 4 Chrome versions', | ||||
|         'last 4 Firefox versions', | ||||
|         'last 4 Edge versions', | ||||
|         'last 4 Safari versions', | ||||
|         'last 4 Android versions', | ||||
|         'last 4 ChromeAndroid versions', | ||||
|         'last 4 FirefoxAndroid versions', | ||||
|         'last 4 iOS versions' | ||||
|       ] | ||||
|     }) | ||||
|         "last 4 Chrome versions", | ||||
|         "last 4 Firefox versions", | ||||
|         "last 4 Edge versions", | ||||
|         "last 4 Safari versions", | ||||
|         "last 4 Android versions", | ||||
|         "last 4 ChromeAndroid versions", | ||||
|         "last 4 FirefoxAndroid versions", | ||||
|         "last 4 iOS versions", | ||||
|       ], | ||||
|     }), | ||||
|  | ||||
|     // https://github.com/elchininet/postcss-rtlcss | ||||
|     // If you want to support RTL css, then | ||||
| @@ -23,5 +23,5 @@ module.exports = { | ||||
|     // 2. optionally set quasar.config.js > framework > lang to an RTL language | ||||
|     // 3. uncomment the following line: | ||||
|     // require('postcss-rtlcss') | ||||
|   ] | ||||
| } | ||||
|   ], | ||||
| }; | ||||
|   | ||||
| @@ -12,6 +12,25 @@ export async function fetchUsers(params = {}) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| export async function resetPass(pass) { | ||||
|   const payload = { password: pass }; | ||||
|   try { | ||||
|     const { data } = await axios.put(`${baseUrl}/resetpw/`, payload); | ||||
|     return data; | ||||
|   } catch (e) { | ||||
|     console.error(e); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export async function resetTwoFactor() { | ||||
|   try { | ||||
|     const { data } = await axios.put(`${baseUrl}/reset2fa/`); | ||||
|     return data; | ||||
|   } catch (e) { | ||||
|     console.error(e); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // role api function | ||||
| export async function fetchRoles(params = {}) { | ||||
|   try { | ||||
|   | ||||
| @@ -374,16 +374,10 @@ export default { | ||||
|         "make_model", | ||||
|         "physical_disks", | ||||
|       ]; | ||||
|  | ||||
|       // quasar filter only does visible columns so this is a hack to add hidden columns we want to filter | ||||
|       for (const elem of hiddenFields) { | ||||
|         if (!cols.find((o) => o.name === elem)) { | ||||
|           cols.push({ | ||||
|             name: elem, | ||||
|             field: elem, | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|       // originally I was modifying cols directly but this led to phantom colum so doing it this way now | ||||
|       // https://github.com/amidaware/tacticalrmm/issues/1264 | ||||
|       const allColumns = [...cols, ...hiddenFields.map((field) => ({ field }))]; | ||||
|  | ||||
|       const lowerTerms = terms ? terms.toLowerCase() : ""; | ||||
|       let advancedFilter = false; | ||||
| @@ -437,7 +431,7 @@ export default { | ||||
|         } | ||||
|  | ||||
|         // Normal text filter | ||||
|         return cols.some((col) => { | ||||
|         return allColumns.some((col) => { | ||||
|           const val = cellValue(col, row) + ""; | ||||
|           const haystack = | ||||
|             val === "undefined" || val === "null" ? "" : val.toLowerCase(); | ||||
|   | ||||
							
								
								
									
										75
									
								
								src/components/accounts/ResetPass.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/components/accounts/ResetPass.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| <template> | ||||
|   <q-dialog ref="dialogRef" @hide="onDialogHide"> | ||||
|     <q-card class="q-dialog-plugin" style="width: 60vw"> | ||||
|       <q-card-section class="row"> | ||||
|         <div class="col-3">New password:</div> | ||||
|         <div class="col-9"> | ||||
|           <q-input | ||||
|             outlined | ||||
|             dense | ||||
|             v-model="pass" | ||||
|             :type="isPwd ? 'password' : 'text'" | ||||
|             :rules="[(val) => !!val || '*Required']" | ||||
|           > | ||||
|             <template v-slot:append> | ||||
|               <q-icon | ||||
|                 :name="isPwd ? 'visibility_off' : 'visibility'" | ||||
|                 class="cursor-pointer" | ||||
|                 @click="isPwd = !isPwd" | ||||
|               /> | ||||
|             </template> | ||||
|           </q-input> | ||||
|         </div> | ||||
|         <div class="col-3">Confirm password:</div> | ||||
|         <div class="col-9"> | ||||
|           <q-input | ||||
|             outlined | ||||
|             dense | ||||
|             v-model="pass2" | ||||
|             :type="isPwd ? 'password' : 'text'" | ||||
|             :rules="[(val) => val === pass || 'Passwords do not match']" | ||||
|           > | ||||
|             <template v-slot:append> | ||||
|               <q-icon | ||||
|                 :name="isPwd ? 'visibility_off' : 'visibility'" | ||||
|                 class="cursor-pointer" | ||||
|                 @click="isPwd = !isPwd" | ||||
|               /> | ||||
|             </template> | ||||
|           </q-input> | ||||
|         </div> | ||||
|       </q-card-section> | ||||
|       <q-card-actions align="right"> | ||||
|         <q-btn | ||||
|           color="primary" | ||||
|           label="Reset" | ||||
|           @click="onOKClick" | ||||
|           :disable="!pass || pass !== pass2" | ||||
|         /> | ||||
|         <q-btn color="negative" label="Cancel" @click="onDialogCancel" /> | ||||
|       </q-card-actions> | ||||
|     </q-card> | ||||
|   </q-dialog> | ||||
| </template> | ||||
|  | ||||
| <script setup> | ||||
| import { ref } from "vue"; | ||||
| import { useDialogPluginComponent } from "quasar"; | ||||
| import { resetPass } from "@/api/accounts"; | ||||
| import { notifySuccess } from "@/utils/notify"; | ||||
|  | ||||
| const pass = ref(""); | ||||
| const pass2 = ref(""); | ||||
| const isPwd = ref(true); | ||||
|  | ||||
| defineEmits([...useDialogPluginComponent.emits]); | ||||
|  | ||||
| const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = | ||||
|   useDialogPluginComponent(); | ||||
|  | ||||
| async function onOKClick() { | ||||
|   const ret = await resetPass(pass.value); | ||||
|   notifySuccess(ret); | ||||
|   onDialogOK(); | ||||
| } | ||||
| </script> | ||||
| @@ -166,7 +166,7 @@ export default { | ||||
|           type: "textarea", | ||||
|           isValid: (val) => !!val, | ||||
|         }, | ||||
|         style: "width: 30vw; max-width: 50vw;", | ||||
|         style: "width: 90vw; max-width: 90vw", | ||||
|         ok: { label: "Add" }, | ||||
|         cancel: true, | ||||
|       }).onOk(async () => { | ||||
| @@ -193,7 +193,7 @@ export default { | ||||
|           type: "textarea", | ||||
|           isValid: (val) => !!val, | ||||
|         }, | ||||
|         style: "width: 30vw; max-width: 50vw;", | ||||
|         style: "width: 90vw; max-width: 90vw", | ||||
|         ok: { label: "Save" }, | ||||
|         cancel: true, | ||||
|       }).onOk(async (data) => { | ||||
|   | ||||
| @@ -8,16 +8,16 @@ | ||||
|             v | ||||
|           }}</q-badge> | ||||
|           <q-btn | ||||
|               v-if="!!v" | ||||
|               size="sm" | ||||
|               class="q-ml-xs" | ||||
|               flat | ||||
|               round | ||||
|               icon="content_copy" | ||||
|               @click="copyValueToClip(v)" | ||||
|             > | ||||
|               <q-tooltip>Copy to Clipboard</q-tooltip> | ||||
|             </q-btn> | ||||
|             v-if="!!v" | ||||
|             size="sm" | ||||
|             class="q-ml-xs" | ||||
|             flat | ||||
|             round | ||||
|             icon="content_copy" | ||||
|             @click="copyValueToClip(v)" | ||||
|           > | ||||
|             <q-tooltip>Copy to Clipboard</q-tooltip> | ||||
|           </q-btn> | ||||
|         </div> | ||||
|       </div> | ||||
|       <q-separator v-if="info.length > 1" /> | ||||
| @@ -42,10 +42,9 @@ export default { | ||||
|     const tabHeight = computed(() => store.state.tabHeight); | ||||
|  | ||||
|     function copyValueToClip(val) { | ||||
|       copyToClipboard(val) | ||||
|         .then(() => { | ||||
|           notifySuccess("Copied to clipboard"); | ||||
|         }) | ||||
|       copyToClipboard(val).then(() => { | ||||
|         notifySuccess("Copied to clipboard"); | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|   | ||||
| @@ -128,7 +128,7 @@ import { useDialogPluginComponent } from "quasar"; | ||||
| import { useCheckModal } from "@/composables/checks"; | ||||
| import { useScriptDropdown } from "@/composables/scripts"; | ||||
| import { validateRetcode } from "@/utils/validation"; | ||||
| import { envVarsLabel } from "@/constants/constants" | ||||
| import { envVarsLabel } from "@/constants/constants"; | ||||
|  | ||||
| // ui imports | ||||
| import TacticalDropdown from "@/components/ui/TacticalDropdown.vue"; | ||||
| @@ -146,10 +146,15 @@ export default { | ||||
|     const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent(); | ||||
|  | ||||
|     // setup script dropdown | ||||
|     const { script, scriptOptions, defaultTimeout, defaultArgs, defaultEnvVars } = | ||||
|       useScriptDropdown(props.check ? props.check.script : undefined, { | ||||
|         onMount: true, | ||||
|       }); | ||||
|     const { | ||||
|       script, | ||||
|       scriptOptions, | ||||
|       defaultTimeout, | ||||
|       defaultArgs, | ||||
|       defaultEnvVars, | ||||
|     } = useScriptDropdown(props.check ? props.check.script : undefined, { | ||||
|       onMount: true, | ||||
|     }); | ||||
|  | ||||
|     // check logic | ||||
|     const { state, loading, submit, failOptions, severityOptions } = | ||||
|   | ||||
| @@ -122,7 +122,7 @@ export default { | ||||
|  | ||||
|       try { | ||||
|         const result = props.APIKey | ||||
|           ? await editAPIKey(data) | ||||
|           ? await editAPIKey(data.id, data) | ||||
|           : await saveAPIKey(data); | ||||
|         onDialogOK(); | ||||
|         notifySuccess(result); | ||||
|   | ||||
| @@ -89,7 +89,8 @@ | ||||
|       <p class="text-italic"> | ||||
|         Note: the auth token above will be valid for {{ info.expires }} hours. | ||||
|       </p> | ||||
|       <q-btn v-if="info.plat === 'windows'" | ||||
|       <q-btn | ||||
|         v-if="info.plat === 'windows'" | ||||
|         type="a" | ||||
|         :href="info.data.url" | ||||
|         color="primary" | ||||
|   | ||||
| @@ -221,11 +221,18 @@ export default { | ||||
|     const { dialogRef, onDialogHide } = useDialogPluginComponent(); | ||||
|  | ||||
|     // setup dropdowns | ||||
|     const { script, scriptOptions, defaultTimeout, defaultArgs, defaultEnvVars, syntax, link } = | ||||
|       useScriptDropdown(props.script, { | ||||
|         onMount: true, | ||||
|         filterByPlatform: props.agent.plat, | ||||
|       }); | ||||
|     const { | ||||
|       script, | ||||
|       scriptOptions, | ||||
|       defaultTimeout, | ||||
|       defaultArgs, | ||||
|       defaultEnvVars, | ||||
|       syntax, | ||||
|       link, | ||||
|     } = useScriptDropdown(props.script, { | ||||
|       onMount: true, | ||||
|       filterByPlatform: props.agent.plat, | ||||
|     }); | ||||
|     const { customFieldOptions } = useCustomFieldDropdown({ onMount: true }); | ||||
|  | ||||
|     // main run script functionaity | ||||
|   | ||||
| @@ -836,10 +836,15 @@ export default { | ||||
|     const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent(); | ||||
|  | ||||
|     // setup dropdowns | ||||
|     const { script, scriptOptions, defaultTimeout, defaultArgs, defaultEnvVars } = | ||||
|       useScriptDropdown(undefined, { | ||||
|         onMount: true, | ||||
|       }); | ||||
|     const { | ||||
|       script, | ||||
|       scriptOptions, | ||||
|       defaultTimeout, | ||||
|       defaultArgs, | ||||
|       defaultEnvVars, | ||||
|     } = useScriptDropdown(undefined, { | ||||
|       onMount: true, | ||||
|     }); | ||||
|  | ||||
|     // set defaultTimeout to 30 | ||||
|     defaultTimeout.value = 30; | ||||
|   | ||||
| @@ -30,7 +30,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) { | ||||
|       ); | ||||
|       defaultTimeout.value = tmpScript.timeout; | ||||
|       defaultArgs.value = tmpScript.args; | ||||
|       defaultEnvVars.value = tmpScript.env_vars, | ||||
|       defaultEnvVars.value = tmpScript.env_vars; | ||||
|       syntax.value = tmpScript.syntax; | ||||
|       link.value = | ||||
|         tmpScript.script_type === "builtin" | ||||
|   | ||||
| @@ -6,5 +6,5 @@ export const GOARCH_ARM32 = "arm"; | ||||
| export const runAsUserToolTip = | ||||
|   "Run in the context of the logged in user. If no user is logged in, the script will not run and an error will be returned."; | ||||
|  | ||||
| export const envVarsLabel =  | ||||
|   "Environment vars (press Enter after typing each key=value pair)" | ||||
| export const envVarsLabel = | ||||
|   "Environment vars (press Enter after typing each key=value pair)"; | ||||
|   | ||||
| @@ -19,10 +19,15 @@ | ||||
|         inline-actions | ||||
|         class="bg-yellow text-black text-center" | ||||
|       > | ||||
|       <q-icon size="xl" name="warning" /> | ||||
|         <span><br />Your code signing token is no longer valid.<br/><br/> | ||||
|         If you have downgraded or cancelled your sponsorship, please delete your token from the Code Signing modal and refresh to get rid of this banner.<br/><br/> | ||||
|         For any issues or to renew your sponsorship please email support@amidaware.com<br/><br/></span> | ||||
|         <q-icon size="xl" name="warning" /> | ||||
|         <span | ||||
|           ><br />Your code signing token is no longer valid.<br /><br /> | ||||
|           If you have downgraded or cancelled your sponsorship, please delete | ||||
|           your token from the Code Signing modal and refresh to get rid of this | ||||
|           banner.<br /><br /> | ||||
|           For any issues or to renew your sponsorship please email | ||||
|           support@amidaware.com<br /><br | ||||
|         /></span> | ||||
|         <q-btn | ||||
|           color="dark" | ||||
|           icon="refresh" | ||||
| @@ -135,6 +140,32 @@ | ||||
|                 <q-item-label>Preferences</q-item-label> | ||||
|               </q-item-section> | ||||
|             </q-item> | ||||
|             <q-item clickable> | ||||
|               <q-item-section>Account</q-item-section> | ||||
|               <q-item-section side> | ||||
|                 <q-icon name="keyboard_arrow_right" /> | ||||
|               </q-item-section> | ||||
|  | ||||
|               <q-menu anchor="top end" self="top start"> | ||||
|                 <q-list> | ||||
|                   <q-item | ||||
|                     clickable | ||||
|                     v-ripple | ||||
|                     @click="resetPassword" | ||||
|                     v-close-popup | ||||
|                   > | ||||
|                     <q-item-section> | ||||
|                       <q-item-label>Reset Password</q-item-label> | ||||
|                     </q-item-section> | ||||
|                   </q-item> | ||||
|                   <q-item clickable v-ripple @click="reset2FA" v-close-popup> | ||||
|                     <q-item-section> | ||||
|                       <q-item-label>Reset 2FA</q-item-label> | ||||
|                     </q-item-section> | ||||
|                   </q-item> | ||||
|                 </q-list> | ||||
|               </q-menu> | ||||
|             </q-item> | ||||
|             <q-item to="/expired" exact> | ||||
|               <q-item-section> | ||||
|                 <q-item-label>Logout</q-item-label> | ||||
| @@ -156,10 +187,13 @@ import { useQuasar } from "quasar"; | ||||
| import { useStore } from "vuex"; | ||||
| import axios from "axios"; | ||||
| import { getWSUrl } from "@/websocket/channels"; | ||||
| import { resetTwoFactor } from "@/api/accounts"; | ||||
| import { notifySuccess } from "@/utils/notify"; | ||||
|  | ||||
| // ui imports | ||||
| import AlertsIcon from "@/components/AlertsIcon.vue"; | ||||
| import UserPreferences from "@/components/modals/coresettings/UserPreferences.vue"; | ||||
| import ResetPass from "@/components/accounts/ResetPass.vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "MainLayout", | ||||
| @@ -197,6 +231,26 @@ export default { | ||||
|       }).onOk(() => store.dispatch("getDashInfo")); | ||||
|     } | ||||
|  | ||||
|     function resetPassword() { | ||||
|       $q.dialog({ | ||||
|         component: ResetPass, | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     function reset2FA() { | ||||
|       $q.dialog({ | ||||
|         title: "Reset 2FA", | ||||
|         message: "Are you sure you would like to reset your 2FA token?", | ||||
|         cancel: true, | ||||
|         persistent: true, | ||||
|       }).onOk(async () => { | ||||
|         try { | ||||
|           const ret = await resetTwoFactor(); | ||||
|           notifySuccess(ret, 3000); | ||||
|         } catch {} | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     const serverCount = ref(0); | ||||
|     const serverOfflineCount = ref(0); | ||||
|     const workstationCount = ref(0); | ||||
| @@ -281,6 +335,8 @@ export default { | ||||
|  | ||||
|       // methods | ||||
|       showUserPreferences, | ||||
|       resetPassword, | ||||
|       reset2FA, | ||||
|       updateAvailable, | ||||
|     }; | ||||
|   }, | ||||
|   | ||||
| @@ -173,6 +173,18 @@ | ||||
|                         </q-menu> | ||||
|                       </q-item> | ||||
|  | ||||
|                       <!-- Bulk Run Checks --> | ||||
|                       <q-item | ||||
|                         clickable | ||||
|                         v-close-popup | ||||
|                         @click="runChecks(props.node)" | ||||
|                       > | ||||
|                         <q-item-section side> | ||||
|                           <q-icon name="fas fa-check-double" /> | ||||
|                         </q-item-section> | ||||
|                         <q-item-section>Run Checks</q-item-section> | ||||
|                       </q-item> | ||||
|  | ||||
|                       <q-separator></q-separator> | ||||
|  | ||||
|                       <q-item clickable v-close-popup> | ||||
| @@ -690,6 +702,17 @@ export default { | ||||
|         }) | ||||
|         .onOk(() => this.$store.dispatch("refreshDashboard")); | ||||
|     }, | ||||
|     runChecks(node) { | ||||
|       const target = node.children ? "client" : "site"; | ||||
|       this.$axios | ||||
|         .post(`/checks/${target}/${node.id}/csbulkrun/`) | ||||
|         .then((r) => { | ||||
|           this.notifySuccess(r.data); | ||||
|         }) | ||||
|         .catch((e) => { | ||||
|           console.error(e); | ||||
|         }); | ||||
|     }, | ||||
|     showToggleMaintenance(node) { | ||||
|       let data = { | ||||
|         id: node.id, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user