Compare commits
	
		
			71 Commits
		
	
	
		
			v0.101.19-
			...
			v0.101.31
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 20d534eab0 | ||
|  | 0d87f5afee | ||
|  | 1b83c3c5d6 | ||
|  | 1b2286c4f8 | ||
|  | 34233fde2f | ||
|  | e95dd5f6e7 | ||
|  | 901e6986a0 | ||
|  | aa78929743 | ||
|  | 8207f30234 | ||
|  | 1879977b83 | ||
|  | b4de579a74 | ||
|  | 23f15ff9e5 | ||
|  | 498e038bbb | ||
|  | bb1f1c19cf | ||
|  | 74e3aa4e46 | ||
|  | 07a8e3ebcb | ||
|  | 89966dd006 | ||
|  | 68036f6837 | ||
|  | 45ac82b1dd | ||
|  | d94e5c7965 | ||
|  | e0c1b3199a | ||
|  | fdbbdf7394 | ||
|  | 03fae45ac5 | ||
|  | 346670e8ea | ||
|  | e030efaecf | ||
|  | b8a4f9fe74 | ||
|  | f963b51d70 | ||
|  | feacb19cf9 | ||
|  | 7ce2c1e969 | ||
|  | d1defcef4a | ||
|  | e674b4fa5d | ||
|  | b08a5a6c2d | ||
|  | 9fa1d7209f | ||
|  | 2adfccfa1d | ||
|  | 04766efcd0 | ||
|  | 4babb937f6 | ||
|  | c2591c9e7d | ||
|  | 69403def2a | ||
|  | 3fdd8272f6 | ||
|  | 339227bedc | ||
|  | 17c7c95cc1 | ||
|  | a3ceb5e81b | ||
|  | 679d8cab77 | ||
|  | c4c1474e09 | ||
|  | 82677b0b82 | ||
|  | b78af07f11 | ||
|  | 24acef19c5 | ||
|  | fee6edb39e | ||
|  | 89e7db905d | ||
|  | 827e81dcda | ||
|  | 6ea3a053f2 | ||
|  | 88d297f7c6 | ||
|  | 6c57d3e6b1 | ||
|  | 7fcbe6fbd8 | ||
|  | 0113fbc761 | ||
|  | a2f472ef9c | ||
|  | 8403ac0e93 | ||
|  | b7a91563b0 | ||
|  | ab19afca16 | ||
|  | f24c6a7a80 | ||
|  | 99490bf859 | ||
|  | 72cdeeaa6a | ||
|  | 1eca4d605b | ||
|  | 52ee98f6f8 | ||
|  | d270b877c9 | ||
|  | fd8b2a1d98 | ||
|  | f518043d8d | ||
|  | cc2335558d | ||
|  | a8a171ba2c | ||
|  | 24a63f477e | ||
|  | ddeb6293a1 | 
							
								
								
									
										3
									
								
								.github/workflows/build-release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/build-release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,7 +15,7 @@ jobs: | ||||
|  | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: 16 | ||||
|           node-version: 18 | ||||
|  | ||||
|       - run: touch env-config.js | ||||
|  | ||||
| @@ -32,4 +32,3 @@ jobs: | ||||
|         uses: softprops/action-gh-release@v1 | ||||
|         with: | ||||
|           files: trmm-web-${{github.ref_name}}.tar.gz | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/frontend-linting.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/frontend-linting.yml
									
									
									
									
										vendored
									
									
								
							| @@ -13,7 +13,7 @@ jobs: | ||||
|  | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: 16 | ||||
|           node-version: 18 | ||||
|       - run: npm install | ||||
|  | ||||
|       - name: Run Prettier formatting | ||||
|   | ||||
							
								
								
									
										2034
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2034
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										44
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "web", | ||||
|   "version": "0.101.19-dev", | ||||
|   "version": "0.101.31", | ||||
|   "private": true, | ||||
|   "productName": "Tactical RMM", | ||||
|   "scripts": { | ||||
| @@ -10,31 +10,31 @@ | ||||
|     "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@quasar/extras": "1.16.3", | ||||
|     "apexcharts": "3.40.0", | ||||
|     "axios": "1.4.0", | ||||
|     "dotenv": "16.0.3", | ||||
|     "qrcode.vue": "3.3.4", | ||||
|     "quasar": "2.12.0", | ||||
|     "vue": "3.2.47", | ||||
|     "vue3-ace-editor": "2.2.2", | ||||
|     "vue3-apexcharts": "1.4.1", | ||||
|     "@quasar/extras": "1.16.7", | ||||
|     "apexcharts": "3.41.1", | ||||
|     "axios": "1.5.1", | ||||
|     "dotenv": "16.3.1", | ||||
|     "qrcode.vue": "3.4.1", | ||||
|     "quasar": "2.12.7", | ||||
|     "vue": "3.3.4", | ||||
|     "vue3-ace-editor": "2.2.3", | ||||
|     "vue3-apexcharts": "1.4.4", | ||||
|     "vuedraggable": "4.1.0", | ||||
|     "vue-router": "4.1.6", | ||||
|     "vue-router": "4.2.5", | ||||
|     "vuex": "4.1.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@quasar/cli": "^2.1.0", | ||||
|     "@intlify/unplugin-vue-i18n": "^0.10.0", | ||||
|     "@quasar/app-vite": "^1.3.0", | ||||
|     "@types/node": "^18.16.5", | ||||
|     "@typescript-eslint/eslint-plugin": "^5.59.2", | ||||
|     "@typescript-eslint/parser": "^5.59.2", | ||||
|     "autoprefixer": "10.4.14", | ||||
|     "eslint": "8.40.0", | ||||
|     "eslint-config-prettier": "8.8.0", | ||||
|     "@quasar/cli": "2.3.0", | ||||
|     "@intlify/unplugin-vue-i18n": "1.4.0", | ||||
|     "@quasar/app-vite": "1.6.2", | ||||
|     "@types/node": "20.8.0", | ||||
|     "@typescript-eslint/eslint-plugin": "6.7.3", | ||||
|     "@typescript-eslint/parser": "6.7.3", | ||||
|     "autoprefixer": "10.4.16", | ||||
|     "eslint": "8.50.0", | ||||
|     "eslint-config-prettier": "9.0.0", | ||||
|     "eslint-plugin-vue": "8.7.1", | ||||
|     "prettier": "2.8.8", | ||||
|     "typescript": "5.0.4" | ||||
|     "prettier": "3.0.3", | ||||
|     "typescript": "5.2.2" | ||||
|   } | ||||
| } | ||||
| @@ -12,6 +12,9 @@ export default { | ||||
| body | ||||
|   overflow-y: hidden | ||||
|  | ||||
| a | ||||
|   color: #1976D2 | ||||
|  | ||||
| .tbl-sticky | ||||
|   thead tr th | ||||
|     position: sticky | ||||
|   | ||||
| @@ -232,3 +232,8 @@ export async function removeAgentNote(pk) { | ||||
|   const { data } = await axios.delete(`${baseUrl}/notes/${pk}/`); | ||||
|   return data; | ||||
| } | ||||
|  | ||||
| export async function wakeUpWOL(agent_id) { | ||||
|   const { data } = await axios.post(`${baseUrl}/${agent_id}/wol/`); | ||||
|   return data; | ||||
| } | ||||
|   | ||||
| @@ -31,6 +31,11 @@ export async function resetCheck(id) { | ||||
|   return data; | ||||
| } | ||||
|  | ||||
| export async function resetAllChecksStatus(agent_id) { | ||||
|   const { data } = await axios.post(`${baseUrl}/${agent_id}/resetall/`); | ||||
|   return data; | ||||
| } | ||||
|  | ||||
| export async function runAgentChecks(agent_id) { | ||||
|   const { data } = await axios.post(`${baseUrl}/${agent_id}/run/`); | ||||
|   return data; | ||||
|   | ||||
| @@ -211,7 +211,7 @@ | ||||
|               v-if="props.row.maintenance_mode" | ||||
|               name="construction" | ||||
|               size="1.2em" | ||||
|               color="green" | ||||
|               :color="dash_positive_color" | ||||
|             > | ||||
|               <q-tooltip>Maintenance Mode Enabled</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -219,7 +219,7 @@ | ||||
|               v-else-if="props.row.checks.failing > 0" | ||||
|               name="fas fa-check-double" | ||||
|               size="1.2em" | ||||
|               color="negative" | ||||
|               :color="dash_negative_color" | ||||
|             > | ||||
|               <q-tooltip>Checks failing</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -227,7 +227,7 @@ | ||||
|               v-else-if="props.row.checks.warning > 0" | ||||
|               name="fas fa-check-double" | ||||
|               size="1.2em" | ||||
|               color="warning" | ||||
|               :color="dash_warning_color" | ||||
|             > | ||||
|               <q-tooltip>Checks warning</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -235,7 +235,7 @@ | ||||
|               v-else-if="props.row.checks.info > 0" | ||||
|               name="fas fa-check-double" | ||||
|               size="1.2em" | ||||
|               color="info" | ||||
|               :color="dash_info_color" | ||||
|             > | ||||
|               <q-tooltip>Checks info</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -243,7 +243,7 @@ | ||||
|               v-else | ||||
|               name="fas fa-check-double" | ||||
|               size="1.2em" | ||||
|               color="positive" | ||||
|               :color="dash_positive_color" | ||||
|             > | ||||
|               <q-tooltip>Checks passing</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -279,7 +279,7 @@ | ||||
|               @click="showPendingActionsModal(props.row)" | ||||
|               name="far fa-clock" | ||||
|               size="1.4em" | ||||
|               color="warning" | ||||
|               :color="dash_warning_color" | ||||
|               class="cursor-pointer" | ||||
|             > | ||||
|               <q-tooltip | ||||
| @@ -303,7 +303,7 @@ | ||||
|               v-if="props.row.status === 'overdue'" | ||||
|               name="fas fa-signal" | ||||
|               size="1.2em" | ||||
|               color="negative" | ||||
|               :color="dash_negative_color" | ||||
|             > | ||||
|               <q-tooltip>Agent overdue</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -311,11 +311,16 @@ | ||||
|               v-else-if="props.row.status === 'offline'" | ||||
|               name="fas fa-signal" | ||||
|               size="1.2em" | ||||
|               color="warning" | ||||
|               :color="dash_warning_color" | ||||
|             > | ||||
|               <q-tooltip>Agent offline</q-tooltip> | ||||
|             </q-icon> | ||||
|             <q-icon v-else name="fas fa-signal" size="1.2em" color="positive"> | ||||
|             <q-icon | ||||
|               v-else | ||||
|               name="fas fa-signal" | ||||
|               size="1.2em" | ||||
|               :color="dash_positive_color" | ||||
|             > | ||||
|               <q-tooltip>Agent online</q-tooltip> | ||||
|             </q-icon> | ||||
|           </q-td> | ||||
| @@ -373,7 +378,8 @@ export default { | ||||
|         "local_ips", | ||||
|         "make_model", | ||||
|         "physical_disks", | ||||
|         "custom_fields" | ||||
|         "custom_fields", | ||||
|         "serial_number", | ||||
|       ]; | ||||
|       // quasar filter only does visible columns so this is a hack to add hidden columns we want to filter | ||||
|       // originally I was modifying cols directly but this led to phantom colum so doing it this way now | ||||
| @@ -435,7 +441,7 @@ export default { | ||||
|         return allColumns.some((col) => { | ||||
|           let valObj = cellValue(col, row); | ||||
|           if (Array.isArray(valObj)) { | ||||
|             valObj = valObj.map((item) => item.value ? item.value : item); | ||||
|             valObj = valObj.map((item) => (item.value ? item.value : item)); | ||||
|           } | ||||
|           const val = valObj + ""; | ||||
|           const haystack = | ||||
| @@ -488,7 +494,9 @@ export default { | ||||
|       const data = { | ||||
|         [db_field]: !alert_action, | ||||
|       }; | ||||
|       const alertColor = !alert_action ? "positive" : "info"; | ||||
|       const alertColor = !alert_action | ||||
|         ? this.dash_positive_color | ||||
|         : this.dash_info_color; | ||||
|       this.$axios.put(`/agents/${agent.agent_id}/`, data).then(() => { | ||||
|         this.$q.notify({ | ||||
|           color: alertColor, | ||||
| @@ -532,7 +540,13 @@ export default { | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(["tableHeight"]), | ||||
|     ...mapState([ | ||||
|       "tableHeight", | ||||
|       "dash_info_color", | ||||
|       "dash_positive_color", | ||||
|       "dash_negative_color", | ||||
|       "dash_warning_color", | ||||
|     ]), | ||||
|     agentDblClickAction() { | ||||
|       return this.$store.state.agentDblClickAction; | ||||
|     }, | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|     <q-badge v-if="alertsCount > 0" :color="badgeColor" floating transparent>{{ | ||||
|       alertsCountText() | ||||
|     }}</q-badge> | ||||
|     <q-menu style="max-height: 30vh"> | ||||
|     <q-menu :style="{ 'max-height': `${$q.screen.height - 100}px` }"> | ||||
|       <q-list separator> | ||||
|         <q-item v-if="alertsCount === 0">No New Alerts</q-item> | ||||
|         <q-item v-for="alert in topAlerts" :key="alert.id"> | ||||
| @@ -59,6 +59,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapState } from "vuex"; | ||||
| import mixins from "@/mixins/mixins"; | ||||
| import AlertsOverview from "@/components/modals/alerts/AlertsOverview.vue"; | ||||
| import { getTimeLapse } from "@/utils/format"; | ||||
| @@ -75,19 +76,21 @@ export default { | ||||
|     return { | ||||
|       alertsCount: 0, | ||||
|       topAlerts: [], | ||||
|       errorColor: "red", | ||||
|       warningColor: "orange", | ||||
|       infoColor: "blue", | ||||
|       poll: null, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState([ | ||||
|       "dash_info_color", | ||||
|       "dash_warning_color", | ||||
|       "dash_negative_color", | ||||
|     ]), | ||||
|     badgeColor() { | ||||
|       const severities = this.topAlerts.map((alert) => alert.severity); | ||||
|  | ||||
|       if (severities.includes("error")) return this.errorColor; | ||||
|       else if (severities.includes("warning")) return this.warningColor; | ||||
|       else return this.infoColor; | ||||
|       if (severities.includes("error")) return this.dash_negative_color; | ||||
|       else if (severities.includes("warning")) return this.dash_warning_color; | ||||
|       else return this.dash_info_color; | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
| @@ -159,9 +162,9 @@ export default { | ||||
|         }); | ||||
|     }, | ||||
|     alertIconColor(severity) { | ||||
|       if (severity === "error") return this.errorColor; | ||||
|       else if (severity === "warning") return this.warningColor; | ||||
|       else return this.infoColor; | ||||
|       if (severity === "error") return this.dash_negative_color; | ||||
|       else if (severity === "warning") return this.dash_warning_color; | ||||
|       else return this.dash_info_color; | ||||
|     }, | ||||
|     alertsCountText() { | ||||
|       if (this.alertsCount > 99) return "99+"; | ||||
|   | ||||
| @@ -98,6 +98,10 @@ | ||||
|                 v-model="localRole.can_reboot_agents" | ||||
|                 label="Reboot Agents" | ||||
|               /> | ||||
|               <q-checkbox | ||||
|                 v-model="localRole.can_send_wol" | ||||
|                 label="Wake-Up (WoL) Agents" | ||||
|               /> | ||||
|               <q-checkbox | ||||
|                 v-model="localRole.can_install_agents" | ||||
|                 label="Install Agents" | ||||
| @@ -437,8 +441,8 @@ export default { | ||||
|           can_run_scripts: false, | ||||
|           can_run_bulk: false, | ||||
|           can_manage_winsvcs: false, | ||||
|           can_recover_agents: false, | ||||
|           can_list_agent_history: false, | ||||
|           can_send_wol: false, | ||||
|           // software perms | ||||
|           can_list_software: false, | ||||
|           can_manage_software: false, | ||||
|   | ||||
| @@ -146,6 +146,13 @@ | ||||
|       <q-item-section>Run Checks</q-item-section> | ||||
|     </q-item> | ||||
|  | ||||
|     <q-item clickable v-close-popup @click="wakeUp(agent)"> | ||||
|       <q-item-section side> | ||||
|         <q-icon size="xs" name="offline_bolt" /> | ||||
|       </q-item-section> | ||||
|       <q-item-section>Wake-Up (WoL)</q-item-section> | ||||
|     </q-item> | ||||
|  | ||||
|     <q-item clickable> | ||||
|       <q-item-section side> | ||||
|         <q-icon size="xs" name="power_settings_new" /> | ||||
| @@ -210,6 +217,7 @@ import { | ||||
|   removeAgent, | ||||
|   runRemoteBackground, | ||||
|   runTakeControl, | ||||
|   wakeUpWOL, | ||||
| } from "@/api/agents"; | ||||
| import { runAgentUpdateScan, runAgentUpdateInstall } from "@/api/winupdates"; | ||||
| import { runAgentChecks } from "@/api/checks"; | ||||
| @@ -370,6 +378,15 @@ export default { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     async function wakeUp(agent) { | ||||
|       try { | ||||
|         const data = await wakeUpWOL(agent.agent_id); | ||||
|         notifySuccess(data); | ||||
|       } catch (e) { | ||||
|         console.error(e); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     function showRebootLaterModal(agent) { | ||||
|       $q.dialog({ | ||||
|         component: RebootLater, | ||||
| @@ -498,6 +515,7 @@ export default { | ||||
|       showPolicyAdd, | ||||
|       showAgentRecovery, | ||||
|       pingAgent, | ||||
|       wakeUp, | ||||
|     }; | ||||
|   }, | ||||
| }; | ||||
|   | ||||
| @@ -261,7 +261,7 @@ | ||||
|           <q-td v-else-if="props.row.task_result.status === 'passing'"> | ||||
|             <q-icon | ||||
|               style="font-size: 1.3rem" | ||||
|               color="positive" | ||||
|               :color="dash_positive_color" | ||||
|               name="check_circle" | ||||
|             > | ||||
|               <q-tooltip>Passing</q-tooltip> | ||||
| @@ -271,7 +271,7 @@ | ||||
|             <q-icon | ||||
|               v-if="props.row.alert_severity === 'info'" | ||||
|               style="font-size: 1.3rem" | ||||
|               color="info" | ||||
|               :color="dash_info_color" | ||||
|               name="info" | ||||
|             > | ||||
|               <q-tooltip>Informational</q-tooltip> | ||||
| @@ -279,7 +279,7 @@ | ||||
|             <q-icon | ||||
|               v-else-if="props.row.alert_severity === 'warning'" | ||||
|               style="font-size: 1.3rem" | ||||
|               color="warning" | ||||
|               :color="dash_warning_color" | ||||
|               name="warning" | ||||
|             > | ||||
|               <q-tooltip>Warning</q-tooltip> | ||||
| @@ -287,7 +287,7 @@ | ||||
|             <q-icon | ||||
|               v-else | ||||
|               style="font-size: 1.3rem" | ||||
|               color="negative" | ||||
|               :color="dash_negative_color" | ||||
|               name="error" | ||||
|             > | ||||
|               <q-tooltip>Error</q-tooltip> | ||||
| @@ -418,6 +418,10 @@ export default { | ||||
|     const tabHeight = computed(() => store.state.tabHeight); | ||||
|     const agentPlatform = computed(() => store.state.agentPlatform); | ||||
|     const formatDate = computed(() => store.getters.formatDate); | ||||
|     const dash_info_color = computed(() => store.state.dash_info_color); | ||||
|     const dash_positive_color = computed(() => store.state.dash_positive_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|  | ||||
|     // setup quasar | ||||
|     const $q = useQuasar(); | ||||
| @@ -552,6 +556,10 @@ export default { | ||||
|       selectedAgent, | ||||
|       tabHeight, | ||||
|       agentPlatform, | ||||
|       dash_info_color, | ||||
|       dash_positive_color, | ||||
|       dash_warning_color, | ||||
|       dash_negative_color, | ||||
|  | ||||
|       // non-reactive data | ||||
|       columns, | ||||
|   | ||||
| @@ -119,6 +119,16 @@ | ||||
|           no-caps | ||||
|           icon="play_arrow" | ||||
|           @click="runChecks" | ||||
|           class="q-mr-md" | ||||
|         /> | ||||
|         <q-btn | ||||
|           label="Reset All Checks Status" | ||||
|           dense | ||||
|           flat | ||||
|           push | ||||
|           no-caps | ||||
|           icon="restart_alt" | ||||
|           @click="resetAllChecks" | ||||
|         /> | ||||
|       </template> | ||||
|  | ||||
| @@ -301,7 +311,7 @@ | ||||
|           <q-td v-else-if="props.row.check_result.status === 'passing'"> | ||||
|             <q-icon | ||||
|               style="font-size: 1.3rem" | ||||
|               color="positive" | ||||
|               :color="dash_positive_color" | ||||
|               name="check_circle" | ||||
|             > | ||||
|               <q-tooltip>Passing</q-tooltip> | ||||
| @@ -311,7 +321,7 @@ | ||||
|             <q-icon | ||||
|               v-if="getAlertSeverity(props.row) === 'info'" | ||||
|               style="font-size: 1.3rem" | ||||
|               color="info" | ||||
|               :color="dash_info_color" | ||||
|               name="info" | ||||
|             > | ||||
|               <q-tooltip>Informational</q-tooltip> | ||||
| @@ -319,7 +329,7 @@ | ||||
|             <q-icon | ||||
|               v-else-if="getAlertSeverity(props.row) === 'warning'" | ||||
|               style="font-size: 1.3rem" | ||||
|               color="warning" | ||||
|               :color="dash_warning_color" | ||||
|               name="warning" | ||||
|             > | ||||
|               <q-tooltip>Warning</q-tooltip> | ||||
| @@ -327,7 +337,7 @@ | ||||
|             <q-icon | ||||
|               v-else | ||||
|               style="font-size: 1.3rem" | ||||
|               color="negative" | ||||
|               :color="dash_negative_color" | ||||
|               name="error" | ||||
|             > | ||||
|               <q-tooltip>Error</q-tooltip> | ||||
| @@ -415,6 +425,7 @@ import { | ||||
|   updateCheck, | ||||
|   removeCheck, | ||||
|   resetCheck, | ||||
|   resetAllChecksStatus, | ||||
|   runAgentChecks, | ||||
| } from "@/api/checks"; | ||||
| import { fetchAgentChecks } from "@/api/agents"; | ||||
| @@ -479,6 +490,10 @@ export default { | ||||
|     const tabHeight = computed(() => store.state.tabHeight); | ||||
|     const agentPlatform = computed(() => store.state.agentPlatform); | ||||
|     const formatDate = computed(() => store.getters.formatDate); | ||||
|     const dash_info_color = computed(() => store.state.dash_info_color); | ||||
|     const dash_positive_color = computed(() => store.state.dash_positive_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|  | ||||
|     // setup quasar | ||||
|     const $q = useQuasar(); | ||||
| @@ -568,7 +583,7 @@ export default { | ||||
|         notifySuccess(result); | ||||
|         refreshDashboard( | ||||
|           false /* clearTreeSelected */, | ||||
|           false /* clearSubTable */ | ||||
|           false /* clearSubTable */, | ||||
|         ); | ||||
|       } catch (e) { | ||||
|         console.error(e); | ||||
| @@ -576,6 +591,27 @@ export default { | ||||
|       loading.value = false; | ||||
|     } | ||||
|  | ||||
|     function resetAllChecks() { | ||||
|       console.info(selectedAgent.value); | ||||
|       $q.dialog({ | ||||
|         title: "Are you sure?", | ||||
|         message: "Reset all checks status", | ||||
|         cancel: true, | ||||
|         ok: { label: "Reset", color: "negative" }, | ||||
|         persistent: true, | ||||
|       }).onOk(async () => { | ||||
|         loading.value = true; | ||||
|         try { | ||||
|           const result = await resetAllChecksStatus(selectedAgent.value); | ||||
|           await getChecks(); | ||||
|           notifySuccess(result); | ||||
|         } catch (e) { | ||||
|           console.error(e); | ||||
|         } | ||||
|         loading.value = false; | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     function showEventInfo(data) { | ||||
|       $q.dialog({ | ||||
|         component: EventLogCheckOutput, | ||||
| @@ -653,6 +689,10 @@ export default { | ||||
|       tabHeight, | ||||
|       selectedAgent, | ||||
|       agentPlatform, | ||||
|       dash_info_color, | ||||
|       dash_positive_color, | ||||
|       dash_warning_color, | ||||
|       dash_negative_color, | ||||
|  | ||||
|       // non-reactive data | ||||
|       columns, | ||||
| @@ -666,6 +706,7 @@ export default { | ||||
|       formatDate, | ||||
|       getAlertSeverity, | ||||
|       runChecks, | ||||
|       resetAllChecks, | ||||
|  | ||||
|       // dialogs | ||||
|       showScriptOutput, | ||||
|   | ||||
| @@ -18,6 +18,33 @@ | ||||
|         icon="refresh" | ||||
|         @click="refreshSummary" | ||||
|       /> | ||||
|       <q-icon | ||||
|         v-if="summary.status === 'overdue'" | ||||
|         name="fas fa-signal" | ||||
|         size="1.2em" | ||||
|         :color="dash_negative_color" | ||||
|         class="q-mr-sm" | ||||
|       > | ||||
|         <q-tooltip>Agent overdue</q-tooltip> | ||||
|       </q-icon> | ||||
|       <q-icon | ||||
|         v-else-if="summary.status === 'offline'" | ||||
|         name="fas fa-signal" | ||||
|         size="1.2em" | ||||
|         :color="dash_warning_color" | ||||
|         class="q-mr-sm" | ||||
|       > | ||||
|         <q-tooltip>Agent offline</q-tooltip> | ||||
|       </q-icon> | ||||
|       <q-icon | ||||
|         v-else | ||||
|         name="fas fa-signal" | ||||
|         size="1.2em" | ||||
|         :color="dash_positive_color" | ||||
|         class="q-mr-sm" | ||||
|       > | ||||
|         <q-tooltip>Agent online</q-tooltip> | ||||
|       </q-icon> | ||||
|       <b>{{ summary.hostname }}</b> | ||||
|       <span v-if="summary.maintenance_mode"> | ||||
|         • <q-badge color="green"> Maintenance Mode </q-badge> | ||||
| @@ -60,7 +87,7 @@ | ||||
|             </q-item-section> | ||||
|             <q-item-section>{{ summary.make_model }}</q-item-section> | ||||
|           </q-item> | ||||
|           <q-item v-for="(cpu, i) in summary.cpu_model" :key="cpu + i"> | ||||
|           <q-item> | ||||
|             <q-item-section avatar> | ||||
|               <q-icon name="fas fa-microchip" /> | ||||
|             </q-item-section> | ||||
| @@ -87,6 +114,13 @@ | ||||
|             </q-item-section> | ||||
|             <q-item-section>{{ summary.graphics }}</q-item-section> | ||||
|           </q-item> | ||||
|           <!-- serial --> | ||||
|           <q-item v-if="serial_number"> | ||||
|             <q-item-section avatar> | ||||
|               <q-icon name="fa-solid fa-barcode" /> | ||||
|             </q-item-section> | ||||
|             <q-item-section>{{ serial_number }}</q-item-section> | ||||
|           </q-item> | ||||
|           <q-item> | ||||
|             <q-item-section avatar> | ||||
|               <q-icon name="fas fa-globe-americas" /> | ||||
| @@ -110,7 +144,7 @@ | ||||
|               size="lg" | ||||
|               square | ||||
|               icon="done" | ||||
|               color="green" | ||||
|               :color="dash_positive_color" | ||||
|               text-color="white" | ||||
|             /> | ||||
|             <small>{{ summary.checks.passing }} checks passing</small> | ||||
| @@ -120,7 +154,7 @@ | ||||
|               size="lg" | ||||
|               square | ||||
|               icon="cancel" | ||||
|               color="red" | ||||
|               :color="dash_negative_color" | ||||
|               text-color="white" | ||||
|             /> | ||||
|             <small>{{ summary.checks.failing }} checks failing</small> | ||||
| @@ -130,7 +164,7 @@ | ||||
|               size="lg" | ||||
|               square | ||||
|               icon="warning" | ||||
|               color="warning" | ||||
|               :color="dash_warning_color" | ||||
|               text-color="white" | ||||
|             /> | ||||
|             <small>{{ summary.checks.warning }} checks warning</small> | ||||
| @@ -140,7 +174,7 @@ | ||||
|               size="lg" | ||||
|               square | ||||
|               icon="info" | ||||
|               color="info" | ||||
|               :color="dash_info_color" | ||||
|               text-color="white" | ||||
|             /> | ||||
|             <small>{{ summary.checks.info }} checks info</small> | ||||
| @@ -222,19 +256,34 @@ export default { | ||||
|     const store = useStore(); | ||||
|     const selectedAgent = computed(() => store.state.selectedRow); | ||||
|     const refreshSummaryTab = computed(() => store.state.refreshSummaryTab); | ||||
|     const dash_info_color = computed(() => store.state.dash_info_color); | ||||
|     const dash_positive_color = computed(() => store.state.dash_positive_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|  | ||||
|     // summary tab logic | ||||
|     const summary = ref(null); | ||||
|     const customFieldsDefinitions = ref(null); | ||||
|     const loading = ref(false); | ||||
|  | ||||
|     const serial_number = computed(() => { | ||||
|       return summary.value.wmi_detail.bios?.[0]?.[0]?.SerialNumber; | ||||
|     }); | ||||
|  | ||||
|     const cpu = computed(() => { | ||||
|       if (summary.value.cpu_model?.length > 1) { | ||||
|         return `${summary.value.cpu_model.length}x ${summary.value.cpu_model[0]}`; | ||||
|       } | ||||
|       return summary.value.cpu_model[0]; | ||||
|     }); | ||||
|  | ||||
|     function diskBarColor(percent) { | ||||
|       if (percent < 80) { | ||||
|         return "positive"; | ||||
|         return dash_positive_color.value; | ||||
|       } else if (percent > 80 && percent < 95) { | ||||
|         return "warning"; | ||||
|         return dash_warning_color.value; | ||||
|       } else { | ||||
|         return "negative"; | ||||
|         return dash_negative_color.value; | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @@ -290,6 +339,7 @@ export default { | ||||
|  | ||||
|     async function refreshSummary() { | ||||
|       loading.value = true; | ||||
|       summary.value = await fetchAgent(selectedAgent.value); | ||||
|       try { | ||||
|         const result = await refreshAgentWMI(selectedAgent.value); | ||||
|         await getSummary(); | ||||
| @@ -325,6 +375,12 @@ export default { | ||||
|       loading, | ||||
|       selectedAgent, | ||||
|       disks, | ||||
|       dash_info_color, | ||||
|       dash_positive_color, | ||||
|       dash_warning_color, | ||||
|       dash_negative_color, | ||||
|       serial_number, | ||||
|       cpu, | ||||
|  | ||||
|       // methods | ||||
|       getSummary, | ||||
|   | ||||
| @@ -128,7 +128,7 @@ | ||||
|             <q-icon | ||||
|               v-else-if="props.row.action === 'ignore'" | ||||
|               name="fas fa-check" | ||||
|               color="negative" | ||||
|               :color="dash_negative_color" | ||||
|             > | ||||
|               <q-tooltip>Ignore</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -144,7 +144,7 @@ | ||||
|             <q-icon | ||||
|               v-if="props.row.installed" | ||||
|               name="fas fa-check" | ||||
|               color="positive" | ||||
|               :color="dash_positive_color" | ||||
|             > | ||||
|               <q-tooltip>Installed</q-tooltip> | ||||
|             </q-icon> | ||||
| @@ -158,11 +158,15 @@ | ||||
|             <q-icon | ||||
|               v-else-if="props.row.action == 'ignore'" | ||||
|               name="fas fa-ban" | ||||
|               color="negative" | ||||
|               :color="dash_negative_color" | ||||
|             > | ||||
|               <q-tooltip>Ignored</q-tooltip> | ||||
|             </q-icon> | ||||
|             <q-icon v-else name="fas fa-exclamation" color="warning"> | ||||
|             <q-icon | ||||
|               v-else | ||||
|               name="fas fa-exclamation" | ||||
|               :color="dash_warning_color" | ||||
|             > | ||||
|               <q-tooltip>Missing</q-tooltip> | ||||
|             </q-icon> | ||||
|           </q-td> | ||||
| @@ -251,6 +255,9 @@ export default { | ||||
|     const tabHeight = computed(() => store.state.tabHeight); | ||||
|     const agentPlatform = computed(() => store.state.agentPlatform); | ||||
|     const formatDate = computed(() => store.getters.formatDate); | ||||
|     const dash_positive_color = computed(() => store.state.dash_positive_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|  | ||||
|     // setup quasar | ||||
|     const $q = useQuasar(); | ||||
| @@ -348,6 +355,9 @@ export default { | ||||
|       selectedAgent, | ||||
|       tabHeight, | ||||
|       agentPlatform, | ||||
|       dash_positive_color, | ||||
|       dash_warning_color, | ||||
|       dash_negative_color, | ||||
|  | ||||
|       // non-reactive data | ||||
|       columns, | ||||
|   | ||||
| @@ -217,6 +217,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapState } from "vuex"; | ||||
| import mixins from "@/mixins/mixins"; | ||||
| import PolicyStatus from "@/components/automation/modals/PolicyStatus.vue"; | ||||
| import DiskSpaceCheck from "@/components/checks/DiskSpaceCheck.vue"; | ||||
| @@ -268,6 +269,9 @@ export default { | ||||
|       if (newValue !== oldValue) this.getChecks(); | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(["dash_positive_color", "dash_warning_color"]), | ||||
|   }, | ||||
|   methods: { | ||||
|     getChecks() { | ||||
|       this.$q.loading.show(); | ||||
| @@ -295,7 +299,9 @@ export default { | ||||
|  | ||||
|       data.check_alert = true; | ||||
|       const act = !action ? "enabled" : "disabled"; | ||||
|       const color = !action ? "positive" : "warning"; | ||||
|       const color = !action | ||||
|         ? this.dash_positive_color | ||||
|         : this.dash_warning_color; | ||||
|       this.$axios | ||||
|         .put(`/checks/${id}/`, data) | ||||
|         .then(() => { | ||||
|   | ||||
| @@ -41,7 +41,7 @@ | ||||
|               <q-td v-if="props.row.status === 'passing'"> | ||||
|                 <q-icon | ||||
|                   style="font-size: 1.3rem" | ||||
|                   color="positive" | ||||
|                   :color="dash_positive_color" | ||||
|                   name="check_circle" | ||||
|                 > | ||||
|                   <q-tooltip>Passing</q-tooltip> | ||||
| @@ -51,7 +51,7 @@ | ||||
|                 <q-icon | ||||
|                   v-if="props.row.alert_severity === 'info'" | ||||
|                   style="font-size: 1.3rem" | ||||
|                   color="info" | ||||
|                   :color="dash_info_color" | ||||
|                   name="info" | ||||
|                 > | ||||
|                   <q-tooltip>Informational</q-tooltip> | ||||
| @@ -59,7 +59,7 @@ | ||||
|                 <q-icon | ||||
|                   v-else-if="props.row.alert_severity === 'warning'" | ||||
|                   style="font-size: 1.3rem" | ||||
|                   color="warning" | ||||
|                   :color="dash_warning_color" | ||||
|                   name="warning" | ||||
|                 > | ||||
|                   <q-tooltip>Warning</q-tooltip> | ||||
| @@ -67,7 +67,7 @@ | ||||
|                 <q-icon | ||||
|                   v-else | ||||
|                   style="font-size: 1.3rem" | ||||
|                   color="negative" | ||||
|                   :color="dash_negative_color" | ||||
|                   name="error" | ||||
|                 > | ||||
|                   <q-tooltip>Error</q-tooltip> | ||||
| @@ -148,7 +148,7 @@ | ||||
|  | ||||
| <script> | ||||
| import { computed } from "vue"; | ||||
| import { useStore } from "vuex"; | ||||
| import { useStore, mapState } from "vuex"; | ||||
| import ScriptOutput from "@/components/checks/ScriptOutput.vue"; | ||||
| import EventLogCheckOutput from "@/components/checks/EventLogCheckOutput.vue"; | ||||
|  | ||||
| @@ -220,6 +220,12 @@ export default { | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState([ | ||||
|       "dash_info_color", | ||||
|       "dash_positive_color", | ||||
|       "dash_negative_color", | ||||
|       "dash_warning_color", | ||||
|     ]), | ||||
|     title() { | ||||
|       return !!this.item.readable_desc | ||||
|         ? this.item.readable_desc + " Status" | ||||
|   | ||||
| @@ -304,6 +304,9 @@ export default { | ||||
|     // setup vuex | ||||
|     const store = useStore(); | ||||
|     const formatDate = computed(() => store.getters.formatDate); | ||||
|     const dash_positive_color = computed(() => store.state.dash_positive_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|  | ||||
|     // setup dropdowns | ||||
|     const { clientOptions, getClientOptions } = useClientDropdown(); | ||||
| @@ -381,12 +384,18 @@ export default { | ||||
|     } | ||||
|  | ||||
|     function formatActionColor(action) { | ||||
|       if (action === "add") return "success"; | ||||
|       else if (action === "agent_install") return "success"; | ||||
|       else if (action === "modify") return "warning"; | ||||
|       else if (action === "delete") return "negative"; | ||||
|       else if (action === "failed_login") return "negative"; | ||||
|       else return "primary"; | ||||
|       switch (action.toLowerCase()) { | ||||
|         case "modify": | ||||
|           return dash_warning_color.value; | ||||
|         case "add": | ||||
|         case "agent_install": | ||||
|           return dash_positive_color.value; | ||||
|         case "delete": | ||||
|         case "failed_login": | ||||
|           return dash_negative_color.value; | ||||
|         default: | ||||
|           return "primary"; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // watchers | ||||
|   | ||||
| @@ -68,25 +68,25 @@ | ||||
|         /> | ||||
|         <q-radio | ||||
|           v-model="logLevelFilter" | ||||
|           color="cyan" | ||||
|           :color="dash_info_color" | ||||
|           val="info" | ||||
|           label="Info" | ||||
|         /> | ||||
|         <q-radio | ||||
|           v-model="logLevelFilter" | ||||
|           color="red" | ||||
|           :color="dash_negative_color" | ||||
|           val="critical" | ||||
|           label="Critical" | ||||
|         /> | ||||
|         <q-radio | ||||
|           v-model="logLevelFilter" | ||||
|           color="red" | ||||
|           :color="dash_negative_color" | ||||
|           val="error" | ||||
|           label="Error" | ||||
|         /> | ||||
|         <q-radio | ||||
|           v-model="logLevelFilter" | ||||
|           color="yellow" | ||||
|           :color="dash_warning_color" | ||||
|           val="warning" | ||||
|           label="Warning" | ||||
|         /> | ||||
| @@ -109,7 +109,7 @@ | ||||
|       <template v-slot:top-row> | ||||
|         <q-tr v-if="Array.isArray(debugLog) && debugLog.length === 1000"> | ||||
|           <q-td colspan="100%"> | ||||
|             <q-icon name="warning" color="warning" /> | ||||
|             <q-icon name="warning" :color="dash_warning_color" /> | ||||
|             Results are limited to 1000 rows. | ||||
|           </q-td> | ||||
|         </q-tr> | ||||
| @@ -203,6 +203,10 @@ export default { | ||||
|     const store = useStore(); | ||||
|  | ||||
|     const formatDate = computed(() => store.getters.formatDate); | ||||
|     const dash_info_color = computed(() => store.state.dash_info_color); | ||||
|     const dash_positive_color = computed(() => store.state.dash_positive_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|  | ||||
|     // setup dropdowns | ||||
|     const { agentOptions, getAgentOptions } = useAgentDropdown(); | ||||
| @@ -261,6 +265,10 @@ export default { | ||||
|       agentOptions, | ||||
|       loading, | ||||
|       filter, | ||||
|       dash_info_color, | ||||
|       dash_positive_color, | ||||
|       dash_warning_color, | ||||
|       dash_negative_color, | ||||
|  | ||||
|       // non-reactive data | ||||
|       columns, | ||||
|   | ||||
| @@ -94,7 +94,7 @@ | ||||
|                         class="q-pr-sm" | ||||
|                         name="fas fa-signal" | ||||
|                         size="1.2em" | ||||
|                         color="warning" | ||||
|                         :color="dash_warning_color" | ||||
|                       /> | ||||
|                       Mark an agent as | ||||
|                       <span class="text-weight-bold">offline</span> if it has | ||||
| @@ -120,7 +120,7 @@ | ||||
|                         class="q-pr-sm" | ||||
|                         name="fas fa-signal" | ||||
|                         size="1.2em" | ||||
|                         color="negative" | ||||
|                         :color="dash_negative_color" | ||||
|                       /> | ||||
|                       Mark an agent as | ||||
|                       <span class="text-weight-bold">overdue</span> if it has | ||||
| @@ -373,6 +373,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapState } from "vuex"; | ||||
| import { useDialogPluginComponent } from "quasar"; | ||||
| import mixins from "@/mixins/mixins"; | ||||
| import PatchPolicyForm from "@/components/modals/agents/PatchPolicyForm.vue"; | ||||
| @@ -549,6 +550,9 @@ export default { | ||||
|       return result.trimEnd(","); | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(["dash_warning_color", "dash_negative_color"]), | ||||
|   }, | ||||
|   mounted() { | ||||
|     // Get custom fields | ||||
|     this.getCustomFields("agent").then((r) => { | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|           <q-tab name="urlactions" label="URL Actions" /> | ||||
|           <q-tab name="retention" label="Retention" /> | ||||
|           <q-tab name="apikeys" label="API Keys" /> | ||||
|           <q-tab name="openai" label="Open AI" /> | ||||
|           <!-- <q-tab name="openai" label="Open AI" /> --> | ||||
|         </q-tabs> | ||||
|       </template> | ||||
|       <template v-slot:after> | ||||
| @@ -511,7 +511,7 @@ | ||||
|               </q-tab-panel> | ||||
|  | ||||
|               <!-- Open AI --> | ||||
|               <q-tab-panel name="openai"> | ||||
|               <!-- <q-tab-panel name="openai"> | ||||
|                 <div class="text-subtitle2">Open AI</div> | ||||
|                 <q-separator /> | ||||
|                 <q-card-section class="row"> | ||||
| @@ -551,7 +551,7 @@ | ||||
|                     </template> | ||||
|                   </q-input> | ||||
|                 </q-card-section> | ||||
|               </q-tab-panel> | ||||
|               </q-tab-panel> --> | ||||
|             </q-tab-panels> | ||||
|           </q-scroll-area> | ||||
|           <q-card-section class="row items-center"> | ||||
|   | ||||
| @@ -82,6 +82,98 @@ | ||||
|                     class="col-4" | ||||
|                   /> | ||||
|                 </q-card-section> | ||||
|                 <q-card-section class="row"> | ||||
|                   <div class="col-2">Dashboard Info Color:</div> | ||||
|                   <div class="col-2"></div> | ||||
|                   <q-input | ||||
|                     outlined | ||||
|                     dense | ||||
|                     v-model="dash_info_color" | ||||
|                     class="col-8" | ||||
|                   > | ||||
|                     <template v-slot:after> | ||||
|                       <q-btn | ||||
|                         round | ||||
|                         dense | ||||
|                         flat | ||||
|                         size="sm" | ||||
|                         icon="info" | ||||
|                         @click="openURL(quasar_color_url)" | ||||
|                       > | ||||
|                         <q-tooltip>Click to see color options</q-tooltip> | ||||
|                       </q-btn> | ||||
|                     </template> | ||||
|                   </q-input> | ||||
|                 </q-card-section> | ||||
|                 <q-card-section class="row"> | ||||
|                   <div class="col-2">Dashboard Positive Color:</div> | ||||
|                   <div class="col-2"></div> | ||||
|                   <q-input | ||||
|                     outlined | ||||
|                     dense | ||||
|                     v-model="dash_positive_color" | ||||
|                     class="col-8" | ||||
|                   > | ||||
|                     <template v-slot:after> | ||||
|                       <q-btn | ||||
|                         round | ||||
|                         dense | ||||
|                         flat | ||||
|                         size="sm" | ||||
|                         icon="info" | ||||
|                         @click="openURL(quasar_color_url)" | ||||
|                       > | ||||
|                         <q-tooltip>Click to see color options</q-tooltip> | ||||
|                       </q-btn> | ||||
|                     </template> | ||||
|                   </q-input> | ||||
|                 </q-card-section> | ||||
|                 <q-card-section class="row"> | ||||
|                   <div class="col-2">Dashboard Negative Color:</div> | ||||
|                   <div class="col-2"></div> | ||||
|                   <q-input | ||||
|                     outlined | ||||
|                     dense | ||||
|                     v-model="dash_negative_color" | ||||
|                     class="col-8" | ||||
|                   > | ||||
|                     <template v-slot:after> | ||||
|                       <q-btn | ||||
|                         round | ||||
|                         dense | ||||
|                         flat | ||||
|                         size="sm" | ||||
|                         icon="info" | ||||
|                         @click="openURL(quasar_color_url)" | ||||
|                       > | ||||
|                         <q-tooltip>Click to see color options</q-tooltip> | ||||
|                       </q-btn> | ||||
|                     </template> | ||||
|                   </q-input> | ||||
|                 </q-card-section> | ||||
|                 <q-card-section class="row"> | ||||
|                   <div class="col-2">Dashboard Warning Color:</div> | ||||
|                   <div class="col-2"></div> | ||||
|                   <q-input | ||||
|                     outlined | ||||
|                     dense | ||||
|                     v-model="dash_warning_color" | ||||
|                     class="col-8" | ||||
|                   > | ||||
|                     <template v-slot:after> | ||||
|                       <q-btn | ||||
|                         round | ||||
|                         dense | ||||
|                         flat | ||||
|                         size="sm" | ||||
|                         icon="info" | ||||
|                         @click="openURL(quasar_color_url)" | ||||
|                       > | ||||
|                         <q-tooltip>Click to see color options</q-tooltip> | ||||
|                       </q-btn> | ||||
|                     </template> | ||||
|                   </q-input> | ||||
|                 </q-card-section> | ||||
|                 <q-card-section class="row"> | ||||
|                   <div class="col-2">Client Sort:</div> | ||||
|                   <div class="col-2"></div> | ||||
| @@ -156,9 +248,14 @@ export default { | ||||
|       tab: "ui", | ||||
|       splitterModel: 20, | ||||
|       loading_bar_color: "", | ||||
|       dash_info_color: "", | ||||
|       dash_positive_color: "", | ||||
|       dash_negative_color: "", | ||||
|       dash_warning_color: "", | ||||
|       urlActions: [], | ||||
|       clear_search_when_switching: true, | ||||
|       date_format: "", | ||||
|       quasar_color_url: "https://quasar.dev/style/color-palette", | ||||
|       clientTreeSortOptions: [ | ||||
|         { | ||||
|           label: "Sort alphabetically, moving failing clients to the top", | ||||
| @@ -235,6 +332,10 @@ export default { | ||||
|         this.defaultAgentTblTab = r.data.default_agent_tbl_tab; | ||||
|         this.clientTreeSort = r.data.client_tree_sort; | ||||
|         this.loading_bar_color = r.data.loading_bar_color; | ||||
|         this.dash_info_color = r.data.dash_info_color; | ||||
|         this.dash_positive_color = r.data.dash_positive_color; | ||||
|         this.dash_negative_color = r.data.dash_negative_color; | ||||
|         this.dash_warning_color = r.data.dash_warning_color; | ||||
|         this.clear_search_when_switching = r.data.clear_search_when_switching; | ||||
|         this.date_format = r.data.date_format; | ||||
|       }); | ||||
| @@ -253,6 +354,10 @@ export default { | ||||
|         default_agent_tbl_tab: this.defaultAgentTblTab, | ||||
|         client_tree_sort: this.clientTreeSort, | ||||
|         loading_bar_color: this.loading_bar_color, | ||||
|         dash_info_color: this.dash_info_color, | ||||
|         dash_positive_color: this.dash_positive_color, | ||||
|         dash_negative_color: this.dash_negative_color, | ||||
|         dash_warning_color: this.dash_warning_color, | ||||
|         clear_search_when_switching: this.clear_search_when_switching, | ||||
|         date_format: this.date_format, | ||||
|       }; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|     ref="dialogRef" | ||||
|     @hide="onDialogHide" | ||||
|     persistent | ||||
|     @keydown.esc="onDialogHide" | ||||
|     @keydown.esc.stop="onDialogHide" | ||||
|     :maximized="maximized" | ||||
|   > | ||||
|     <q-card | ||||
| @@ -173,8 +173,7 @@ | ||||
|                   >Setting this value on the script model will always override | ||||
|                   any 'Run As User' checkboxes in the UI and force this script | ||||
|                   to always be 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. | ||||
|                   user is logged in, the script will run as SYSTEM. | ||||
|                 </q-tooltip> | ||||
|               </q-checkbox> | ||||
|               <q-input | ||||
| @@ -353,7 +352,7 @@ export default { | ||||
|       downloadScript(script.value.id, { with_snippets: props.readonly }).then( | ||||
|         (r) => { | ||||
|           script.value.script_body = r.code; | ||||
|         } | ||||
|         }, | ||||
|       ); | ||||
|  | ||||
|     async function submitForm() { | ||||
|   | ||||
| @@ -286,15 +286,10 @@ | ||||
|           </template> | ||||
|         </q-tree> | ||||
|       </div> | ||||
|       <q-table | ||||
|       <tactical-table | ||||
|         v-if="tableView" | ||||
|         dense | ||||
|         :table-class="{ | ||||
|           'table-bgcolor': !$q.dark.isActive, | ||||
|           'table-bgcolor-dark': $q.dark.isActive, | ||||
|         }" | ||||
|         :style="{ 'max-height': `${$q.screen.height - 182}px` }" | ||||
|         class="tbl-sticky" | ||||
|         :rows="visibleScripts" | ||||
|         :columns="columns" | ||||
|         :loading="loading" | ||||
| @@ -304,6 +299,7 @@ | ||||
|         binary-state-sort | ||||
|         virtual-scroll | ||||
|         :rows-per-page-options="[0]" | ||||
|         column-select | ||||
|       > | ||||
|         <template v-slot:header-cell-favorite="props"> | ||||
|           <q-th :props="props" auto-width> | ||||
| @@ -425,7 +421,7 @@ | ||||
|               </q-list> | ||||
|             </q-menu> | ||||
|             <!-- favorite --> | ||||
|             <q-td> | ||||
|             <q-td key="favorite" :props="props"> | ||||
|               <q-icon | ||||
|                 v-if="props.row.favorite" | ||||
|                 color="yellow-8" | ||||
| @@ -434,7 +430,7 @@ | ||||
|               /> | ||||
|             </q-td> | ||||
|             <!-- shell icon --> | ||||
|             <q-td> | ||||
|             <q-td key="shell" :props="props"> | ||||
|               <q-icon | ||||
|                 v-if="props.row.shell === 'powershell'" | ||||
|                 name="mdi-powershell" | ||||
| @@ -469,7 +465,7 @@ | ||||
|               </q-icon> | ||||
|             </q-td> | ||||
|             <!-- supported platforms --> | ||||
|             <q-td> | ||||
|             <q-td key="supported_platforms" :props="props"> | ||||
|               <q-badge | ||||
|                 v-if=" | ||||
|                   !props.row.supported_platforms || | ||||
| @@ -487,7 +483,11 @@ | ||||
|               > | ||||
|             </q-td> | ||||
|             <!-- name --> | ||||
|             <q-td :style="{ color: props.row.hidden ? 'grey' : '' }"> | ||||
|             <q-td | ||||
|               key="name" | ||||
|               :props="props" | ||||
|               :style="{ color: props.row.hidden ? 'grey' : '' }" | ||||
|             > | ||||
|               {{ truncateText(props.row.name, 50) }} | ||||
|               <q-tooltip | ||||
|                 v-if="props.row.name.length >= 50" | ||||
| @@ -497,7 +497,7 @@ | ||||
|               </q-tooltip> | ||||
|             </q-td> | ||||
|             <!-- args --> | ||||
|             <q-td> | ||||
|             <q-td key="args" :props="props"> | ||||
|               <span v-if="props.row.args.length > 0"> | ||||
|                 {{ truncateText(props.row.args.toString(), 30) }} | ||||
|                 <q-tooltip | ||||
| @@ -509,8 +509,8 @@ | ||||
|               </span> | ||||
|             </q-td> | ||||
|  | ||||
|             <q-td>{{ props.row.category }}</q-td> | ||||
|             <q-td> | ||||
|             <q-td key="category" :props="props">{{ props.row.category }}</q-td> | ||||
|             <q-td key="desc" :props="props"> | ||||
|               {{ truncateText(props.row.description, 30) }} | ||||
|               <q-tooltip | ||||
|                 v-if="props.row.description.length >= 30" | ||||
| @@ -518,10 +518,13 @@ | ||||
|                 >{{ props.row.description }}</q-tooltip | ||||
|               > | ||||
|             </q-td> | ||||
|             <q-td>{{ props.row.default_timeout }}</q-td> | ||||
|             <q-td key="default_timeout" :props="props">{{ | ||||
|               props.row.default_timeout | ||||
|             }}</q-td> | ||||
|             <q-td></q-td> | ||||
|           </q-tr> | ||||
|         </template> | ||||
|       </q-table> | ||||
|       </tactical-table> | ||||
|     </q-card> | ||||
|   </q-dialog> | ||||
| </template> | ||||
| @@ -545,12 +548,13 @@ import { notifySuccess } from "@/utils/notify"; | ||||
| import ScriptUploadModal from "@/components/scripts/ScriptUploadModal.vue"; | ||||
| import ScriptFormModal from "@/components/scripts/ScriptFormModal.vue"; | ||||
| import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue"; | ||||
| import TacticalTable from "@/components/ui/TacticalTable.vue"; | ||||
|  | ||||
| // static data | ||||
| const columns = [ | ||||
|   { | ||||
|     name: "favorite", | ||||
|     label: "", | ||||
|     label: "Favorites", | ||||
|     field: "favorite", | ||||
|     align: "left", | ||||
|     sortable: true, | ||||
| @@ -608,6 +612,9 @@ const columns = [ | ||||
|  | ||||
| export default { | ||||
|   name: "ScriptManager", | ||||
|   components: { | ||||
|     TacticalTable, | ||||
|   }, | ||||
|   emits: [...useDialogPluginComponent.emits], | ||||
|   setup() { | ||||
|     // setup vuex store | ||||
|   | ||||
							
								
								
									
										107
									
								
								src/components/ui/TacticalTable.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/components/ui/TacticalTable.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| <template> | ||||
|   <q-table | ||||
|     :columns="localColumns" | ||||
|     :visible-columns="visibleColumns" | ||||
|     :table-class="{ | ||||
|       'table-bgcolor': !$q.dark.isActive, | ||||
|       'table-bgcolor-dark': $q.dark.isActive, | ||||
|       'column-bgcolor-dark': $q.dark.isActive && columnSelect, | ||||
|       'column-bgcolor': !$q.dark.isActive && columnSelect, | ||||
|       'sticky-header-right-column': columnSelect, | ||||
|       'tbl-sticky': !columnSelect, | ||||
|     }" | ||||
|     v-bind="$attrs" | ||||
|   > | ||||
|     <template v-for="(_, slot) in $slots" v-slot:[slot]="scope"> | ||||
|       <slot :name="slot" v-bind="scope || {}" /> | ||||
|     </template> | ||||
|  | ||||
|     <template v-slot:header-cell-columnSelect="props"> | ||||
|       <q-th :props="props" auto-width> | ||||
|         <q-btn dense flat icon="more_horiz"> | ||||
|           <q-menu> | ||||
|             <q-option-group | ||||
|               v-model="visibleColumns" | ||||
|               :options="columnOptions" | ||||
|               type="checkbox" | ||||
|             /> | ||||
|           </q-menu> | ||||
|         </q-btn> | ||||
|       </q-th> | ||||
|     </template> | ||||
|   </q-table> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from "vue"; | ||||
| export default defineComponent({ | ||||
|   inheritAttrs: false, | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref } from "vue"; | ||||
| import { type QTableColumn } from "quasar"; | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     columns: QTableColumn[]; | ||||
|     columnSelect?: boolean; | ||||
|     excludeColumns?: string[]; | ||||
|   }>(), | ||||
|   { columnSelect: false, excludeColumns: () => ["columnSelect"] } | ||||
| ); | ||||
| // save a non-reactive copy of columns to modify | ||||
| const localColumns: QTableColumn[] = Object.assign([], props.columns); | ||||
| if (props.columnSelect) | ||||
|   localColumns.push({ | ||||
|     name: "columnSelect", | ||||
|     label: "Column Select", | ||||
|     field: "columnSelect", | ||||
|   }); | ||||
| const visibleColumns = ref(localColumns.map((column) => column.name)); | ||||
| const columnOptions = ref( | ||||
|   localColumns | ||||
|     .filter((column) => !props.excludeColumns.includes(column.name)) | ||||
|     .map((column) => ({ label: column.label, value: column.name })) | ||||
| ); | ||||
| </script> | ||||
|  | ||||
| <style lang="sass"> | ||||
|  | ||||
| .column-bgcolor-dark | ||||
|   td:last-child | ||||
|     /* bg color is important for td; just specify one */ | ||||
|     background-color: #1d1d1d | ||||
|  | ||||
| .column-bgcolor | ||||
|   td:last-child | ||||
|     /* bg color is important for td; just specify one */ | ||||
|     background-color: #ffffff | ||||
|  | ||||
| .sticky-header-right-column | ||||
|   tr th | ||||
|     position: sticky | ||||
|     /* higher than z-index for td below */ | ||||
|     z-index: 2 | ||||
|   /* this will be the loading indicator */ | ||||
|   thead tr:last-child th | ||||
|     /* height of all previous header rows */ | ||||
|     top: 48px | ||||
|     /* highest z-index */ | ||||
|     z-index: 3 | ||||
|   thead tr:last-child th | ||||
|     top: 0 | ||||
|     z-index: 1 | ||||
|   tr:last-child th:last-child | ||||
|     /* highest z-index */ | ||||
|     z-index: 3 | ||||
|   td:last-child | ||||
|     z-index: 1 | ||||
|   td:last-child, th:last-child | ||||
|     position: sticky | ||||
|     right: 0 | ||||
|   /* prevent scrolling behind sticky top row on focus */ | ||||
|   tbody | ||||
|     /* height of all previous header rows */ | ||||
|     scroll-margin-top: 48px | ||||
| </style> | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { ref } from "vue"; | ||||
| import { computed, ref } from "vue"; | ||||
| import { useStore } from "vuex"; | ||||
| import { fetchAgents } from "@/api/agents"; | ||||
| import { formatAgentOptions } from "@/utils/format"; | ||||
|  | ||||
| @@ -28,10 +29,12 @@ export function useAgentDropdown() { | ||||
| } | ||||
|  | ||||
| export function cmdPlaceholder(shell) { | ||||
|   if (shell === "cmd") return "rmdir /S /Q C:\\Windows\\System32"; | ||||
|   else if (shell === "powershell") | ||||
|     return "Remove-Item -Recurse -Force C:\\Windows\\System32"; | ||||
|   else return "rm -rf --no-preserve-root /"; | ||||
|   const store = useStore(); | ||||
|   const placeholders = computed(() => store.state.run_cmd_placeholder_text); | ||||
|  | ||||
|   if (shell === "cmd") return placeholders.value.cmd; | ||||
|   else if (shell === "powershell") return placeholders.value.powershell; | ||||
|   else return placeholders.value.shell; | ||||
| } | ||||
|  | ||||
| export const agentPlatformOptions = [ | ||||
|   | ||||
| @@ -4,7 +4,7 @@ export const GOARCH_ARM64 = "arm64"; | ||||
| 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."; | ||||
|   "Run in the context of the logged in user. If no user is logged in, the script will run as SYSTEM"; | ||||
|  | ||||
| export const envVarsLabel = | ||||
|   "Environment vars (press Enter after typing each key=value pair)"; | ||||
|   | ||||
| @@ -56,15 +56,27 @@ | ||||
|           Tactical RMM<span class="text-overline q-ml-sm" | ||||
|             >v{{ currentTRMMVersion }}</span | ||||
|           > | ||||
|           <span class="text-overline q-ml-md" v-if="updateAvailable()" | ||||
|             ><q-badge color="warning" | ||||
|           <!-- update check --> | ||||
|           <q-chip | ||||
|             v-if="updateAvailable" | ||||
|             class="text-overline q-ml-sm" | ||||
|             :color="dash_warning_color" | ||||
|             icon="update" | ||||
|             dense | ||||
|             ><a :href="latestReleaseURL" target="_blank" | ||||
|               >v{{ latestTRMMVersion }} available</a | ||||
|               ></q-badge | ||||
|             ></span | ||||
|             ></q-chip | ||||
|           > | ||||
|           <!-- cert expiring soon check --> | ||||
|           <q-chip | ||||
|             v-if="daysUntilCertExpires <= 15" | ||||
|             dense | ||||
|             :color="dash_negative_color" | ||||
|             text-color="black" | ||||
|             icon="warning" | ||||
|             >SSL certificate expires in {{ daysUntilCertExpires }} days</q-chip | ||||
|           > | ||||
|         </q-toolbar-title> | ||||
|  | ||||
|         <!-- temp dark mode toggle --> | ||||
|         <q-toggle | ||||
|           v-model="darkMode" | ||||
| @@ -94,7 +106,11 @@ | ||||
|               </q-item> | ||||
|               <q-item> | ||||
|                 <q-item-section avatar> | ||||
|                   <q-icon name="power_off" size="sm" color="negative" /> | ||||
|                   <q-icon | ||||
|                     name="power_off" | ||||
|                     size="sm" | ||||
|                     :color="dash_negative_color" | ||||
|                   /> | ||||
|                 </q-item-section> | ||||
|  | ||||
|                 <q-item-section no-wrap> | ||||
| @@ -113,7 +129,11 @@ | ||||
|               </q-item> | ||||
|               <q-item> | ||||
|                 <q-item-section avatar> | ||||
|                   <q-icon name="power_off" size="sm" color="negative" /> | ||||
|                   <q-icon | ||||
|                     name="power_off" | ||||
|                     size="sm" | ||||
|                     :color="dash_negative_color" | ||||
|                   /> | ||||
|                 </q-item-section> | ||||
|  | ||||
|                 <q-item-section no-wrap> | ||||
| @@ -218,6 +238,8 @@ export default { | ||||
|     const user = computed(() => store.state.username); | ||||
|     const hosted = computed(() => store.state.hosted); | ||||
|     const tokenExpired = computed(() => store.state.tokenExpired); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|  | ||||
|     const latestReleaseURL = computed(() => { | ||||
|       return latestTRMMVersion.value | ||||
| @@ -255,6 +277,7 @@ export default { | ||||
|     const serverOfflineCount = ref(0); | ||||
|     const workstationCount = ref(0); | ||||
|     const workstationOfflineCount = ref(0); | ||||
|     const daysUntilCertExpires = ref(100); | ||||
|  | ||||
|     const ws = ref(null); | ||||
|  | ||||
| @@ -262,6 +285,13 @@ export default { | ||||
|       // moved computed token inside the function since it is not refreshing | ||||
|       // when ws is closed causing ws to connect with expired token | ||||
|       const token = computed(() => store.state.token); | ||||
|  | ||||
|       if (!token.value) { | ||||
|         console.log( | ||||
|           "Access token is null or invalid, not setting up WebSocket", | ||||
|         ); | ||||
|         return; | ||||
|       } | ||||
|       console.log("Starting websocket"); | ||||
|       let url = getWSUrl("dashinfo", token.value); | ||||
|       ws.value = new WebSocket(url); | ||||
| @@ -274,6 +304,7 @@ export default { | ||||
|         serverOfflineCount.value = data.total_server_offline_count; | ||||
|         workstationCount.value = data.total_workstation_count; | ||||
|         workstationOfflineCount.value = data.total_workstation_offline_count; | ||||
|         daysUntilCertExpires.value = data.days_until_cert_expires; | ||||
|       }; | ||||
|       ws.value.onclose = (e) => { | ||||
|         try { | ||||
| @@ -294,16 +325,24 @@ export default { | ||||
|  | ||||
|     const poll = ref(null); | ||||
|     function livePoll() { | ||||
|       poll.value = setInterval(() => { | ||||
|       poll.value = setInterval( | ||||
|         () => { | ||||
|           store.dispatch("checkVer"); | ||||
|           store.dispatch("getDashInfo", false); | ||||
|       }, 60 * 5 * 1000); | ||||
|         }, | ||||
|         60 * 4 * 1000, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     function updateAvailable() { | ||||
|       if (latestTRMMVersion.value === "error" || hosted.value) return false; | ||||
|     const updateAvailable = computed(() => { | ||||
|       if ( | ||||
|         latestTRMMVersion.value === "error" || | ||||
|         hosted.value || | ||||
|         currentTRMMVersion.value?.includes("-dev") | ||||
|       ) | ||||
|         return false; | ||||
|       return currentTRMMVersion.value !== latestTRMMVersion.value; | ||||
|     } | ||||
|     }); | ||||
|  | ||||
|     onMounted(() => { | ||||
|       setupWS(); | ||||
| @@ -324,6 +363,7 @@ export default { | ||||
|       serverOfflineCount, | ||||
|       workstationCount, | ||||
|       workstationOfflineCount, | ||||
|       daysUntilCertExpires, | ||||
|       latestReleaseURL, | ||||
|       currentTRMMVersion, | ||||
|       latestTRMMVersion, | ||||
| @@ -332,6 +372,8 @@ export default { | ||||
|       darkMode, | ||||
|       hosted, | ||||
|       tokenExpired, | ||||
|       dash_warning_color, | ||||
|       dash_negative_color, | ||||
|  | ||||
|       // methods | ||||
|       showUserPreferences, | ||||
|   | ||||
| @@ -34,6 +34,15 @@ export default function () { | ||||
|         latestTRMMVersion: null, | ||||
|         dateFormat: "MMM-DD-YYYY - HH:mm", | ||||
|         openAIIntegrationEnabled: false, | ||||
|         dash_info_color: "info", | ||||
|         dash_positive_color: "positive", | ||||
|         dash_negative_color: "negative", | ||||
|         dash_warning_color: "warning", | ||||
|         run_cmd_placeholder_text: { | ||||
|           cmd: "rmdir /S /Q C:\\Windows\\System32", | ||||
|           powershell: "Remove-Item -Recurse -Force C:\\Windows\\System32", | ||||
|           shell: "rm -rf --no-preserve-root /", | ||||
|         }, | ||||
|       }; | ||||
|     }, | ||||
|     getters: { | ||||
| @@ -140,6 +149,21 @@ export default function () { | ||||
|       setOpenAIIntegrationStatus(state, val) { | ||||
|         state.openAIIntegrationEnabled = val; | ||||
|       }, | ||||
|       setDashInfoColor(state, val) { | ||||
|         state.dash_info_color = val; | ||||
|       }, | ||||
|       setDashPositiveColor(state, val) { | ||||
|         state.dash_positive_color = val; | ||||
|       }, | ||||
|       setDashNegativeColor(state, val) { | ||||
|         state.dash_negative_color = val; | ||||
|       }, | ||||
|       setDashWarningColor(state, val) { | ||||
|         state.dash_warning_color = val; | ||||
|       }, | ||||
|       setRunCmdPlaceholders(state, obj) { | ||||
|         state.run_cmd_placeholder_text = obj; | ||||
|       }, | ||||
|     }, | ||||
|     actions: { | ||||
|       setClientTreeSplitter(context, val) { | ||||
| @@ -164,9 +188,9 @@ export default function () { | ||||
|         } | ||||
|         if (clearTreeSelected) commit("destroySubTable"); | ||||
|  | ||||
|         dispatch("getDashInfo", false); | ||||
|         dispatch("loadAgents"); | ||||
|         dispatch("loadTree"); | ||||
|         dispatch("getDashInfo", false); | ||||
|       }, | ||||
|       async loadAgents({ state, commit }) { | ||||
|         commit("AGENT_TABLE_LOADING", true); | ||||
| @@ -198,39 +222,38 @@ export default function () { | ||||
|  | ||||
|         commit("AGENT_TABLE_LOADING", false); | ||||
|       }, | ||||
|       async getDashInfo(context, edited = true) { | ||||
|       async getDashInfo({ commit }, edited = true) { | ||||
|         const { data } = await axios.get("/core/dashinfo/"); | ||||
|         commit("setDashInfoColor", data.dash_info_color); | ||||
|         commit("setDashPositiveColor", data.dash_positive_color); | ||||
|         commit("setDashNegativeColor", data.dash_negative_color); | ||||
|         commit("setDashWarningColor", data.dash_warning_color); | ||||
|         if (edited) { | ||||
|           LoadingBar.setDefaults({ color: data.loading_bar_color }); | ||||
|           context.commit( | ||||
|           commit( | ||||
|             "setClearSearchWhenSwitching", | ||||
|             data.clear_search_when_switching | ||||
|           ); | ||||
|           context.commit( | ||||
|             "SET_DEFAULT_AGENT_TBL_TAB", | ||||
|             data.default_agent_tbl_tab | ||||
|           ); | ||||
|           context.commit("SET_CLIENT_TREE_SORT", data.client_tree_sort); | ||||
|           context.commit("SET_CLIENT_SPLITTER", data.client_tree_splitter); | ||||
|           commit("SET_DEFAULT_AGENT_TBL_TAB", data.default_agent_tbl_tab); | ||||
|           commit("SET_CLIENT_TREE_SORT", data.client_tree_sort); | ||||
|           commit("SET_CLIENT_SPLITTER", data.client_tree_splitter); | ||||
|         } | ||||
|         Dark.set(data.dark_mode); | ||||
|         context.commit("setCurrentTRMMVersion", data.trmm_version); | ||||
|         context.commit("setLatestTRMMVersion", data.latest_trmm_ver); | ||||
|         context.commit("SET_AGENT_DBLCLICK_ACTION", data.dbl_click_action); | ||||
|         context.commit("SET_URL_ACTION", data.url_action); | ||||
|         context.commit("setShowCommunityScripts", data.show_community_scripts); | ||||
|         context.commit("SET_HOSTED", data.hosted); | ||||
|         context.commit("SET_TOKEN_EXPIRED", data.token_is_expired); | ||||
|         context.commit( | ||||
|           "setOpenAIIntegrationStatus", | ||||
|           data.open_ai_integration_enabled | ||||
|         ); | ||||
|         commit("setCurrentTRMMVersion", data.trmm_version); | ||||
|         commit("setLatestTRMMVersion", data.latest_trmm_ver); | ||||
|         commit("SET_AGENT_DBLCLICK_ACTION", data.dbl_click_action); | ||||
|         commit("SET_URL_ACTION", data.url_action); | ||||
|         commit("setShowCommunityScripts", data.show_community_scripts); | ||||
|         commit("SET_HOSTED", data.hosted); | ||||
|         commit("SET_TOKEN_EXPIRED", data.token_is_expired); | ||||
|         commit("setOpenAIIntegrationStatus", data.open_ai_integration_enabled); | ||||
|         commit("setRunCmdPlaceholders", data.run_cmd_placeholder_text); | ||||
|  | ||||
|         if (data.date_format && data.date_format !== "") | ||||
|           context.commit("setDateFormat", data.date_format); | ||||
|         else context.commit("setDateFormat", data.default_date_format); | ||||
|         if (data?.date_format !== "") commit("setDateFormat", data.date_format); | ||||
|         else commit("setDateFormat", data.default_date_format); | ||||
|       }, | ||||
|       loadTree({ commit, state }) { | ||||
|         setTimeout(() => { | ||||
|           axios | ||||
|             .get("/clients/") | ||||
|             .then((r) => { | ||||
| @@ -303,6 +326,7 @@ export default function () { | ||||
|             .catch(() => { | ||||
|               state.treeReady = true; | ||||
|             }); | ||||
|         }, 150); | ||||
|       }, | ||||
|       checkVer(context) { | ||||
|         axios.get("/core/version/").then((r) => { | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|         > | ||||
|           <q-spinner size="40px" color="primary" /> | ||||
|         </div> | ||||
|         <div v-else class="q-pa-sm q-gutter-sm scroll" style="height: 85vh"> | ||||
|         <div v-else class="q-pa-sm q-gutter-sm scroll" style="height: 85vh; overflow: initial;"> | ||||
|           <q-list dense class="rounded-borders"> | ||||
|             <q-item | ||||
|               clickable | ||||
| @@ -452,7 +452,7 @@ export default { | ||||
|       showInstallAgentModal: false, | ||||
|       sitePk: null, | ||||
|       innerModel: (this.$q.screen.height - 82) / 2, | ||||
|       search: (this.$route.query.search ? this.$route.query.search : ""), | ||||
|       search: this.$route.query.search ? this.$route.query.search : "", | ||||
|       filterTextLength: 0, | ||||
|       filterAvailability: "all", | ||||
|       filterPatchesPending: false, | ||||
|   | ||||
| @@ -4,8 +4,17 @@ | ||||
|       <div class="col"></div> | ||||
|       <div class="col"> | ||||
|         <q-card> | ||||
|           <q-card-actions align="center"> | ||||
|             <q-btn | ||||
|               label="Getting Started" | ||||
|               color="info" | ||||
|               class="full-width" | ||||
|               href="https://docs.tacticalrmm.com/guide_gettingstarted/" | ||||
|               target="_blank" | ||||
|             /> | ||||
|           </q-card-actions> | ||||
|           <q-card-section class="row items-center"> | ||||
|             <div class="text-h6">Initial Setup</div> | ||||
|             <div class="text-h5 text-weight-bold">Initial Setup</div> | ||||
|           </q-card-section> | ||||
|           <q-form @submit.prevent="finish"> | ||||
|             <q-card-section> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   <div> | ||||
|     <q-bar> | ||||
|       <span class="text-caption"> | ||||
|         Agent Status: | ||||
|         TRMM Agent Status: | ||||
|         <q-badge :color="statusColor" :label="status" /> | ||||
|       </span> | ||||
|       <q-space /> | ||||
| @@ -15,7 +15,7 @@ | ||||
|         @click="restartMeshService" | ||||
|       /> | ||||
|       <q-btn | ||||
|         color="negative" | ||||
|         :color="dash_negative_color" | ||||
|         size="sm" | ||||
|         label="Recover Connection" | ||||
|         icon="fas fa-first-aid" | ||||
| @@ -35,6 +35,7 @@ | ||||
| <script> | ||||
| // composition imports | ||||
| import { ref, computed, onMounted } from "vue"; | ||||
| import { useStore } from "vuex"; | ||||
| import { useRoute } from "vue-router"; | ||||
| import { useMeta, useQuasar } from "quasar"; | ||||
| import { fetchAgentMeshCentralURLs, sendAgentRecoverMesh } from "@/api/agents"; | ||||
| @@ -47,12 +48,17 @@ export default { | ||||
|   setup() { | ||||
|     // vue lifecycle hooks | ||||
|     onMounted(() => { | ||||
|       dashInfo(); | ||||
|       getDashInfo(); | ||||
|       getMeshURLs(); | ||||
|     }); | ||||
|  | ||||
|     // quasar setup | ||||
|     const $q = useQuasar(); | ||||
|     const store = useStore(); | ||||
|     const dash_positive_color = computed(() => store.state.dash_positive_color); | ||||
|     const dash_negative_color = computed(() => store.state.dash_negative_color); | ||||
|     const dash_warning_color = computed(() => store.state.dash_warning_color); | ||||
|  | ||||
|     // vue router | ||||
|     const { params } = useRoute(); | ||||
| @@ -64,14 +70,19 @@ export default { | ||||
|     const statusColor = computed(() => { | ||||
|       switch (status.value) { | ||||
|         case "online": | ||||
|           return "positive"; | ||||
|           return dash_positive_color.value; | ||||
|         case "offline": | ||||
|           return "warning"; | ||||
|           return dash_warning_color.value; | ||||
|         default: | ||||
|           return "negative"; | ||||
|           return dash_negative_color.value; | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     // TODO refactor this so we're not calling the api twice | ||||
|     const dashInfo = () => { | ||||
|       store.dispatch("getDashInfo", false); | ||||
|     }; | ||||
|  | ||||
|     async function getMeshURLs() { | ||||
|       $q.loading.show(); | ||||
|       try { | ||||
| @@ -131,6 +142,7 @@ export default { | ||||
|       control, | ||||
|       status, | ||||
|       statusColor, | ||||
|       dash_negative_color, | ||||
|  | ||||
|       // methods | ||||
|       repairMeshCentral, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user