Compare commits
	
		
			17 Commits
		
	
	
		
			v0.100.8-d
			...
			v0.101.2-d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 41c61ce152 | ||
|  | 8e9de8b6b6 | ||
|  | 4cf5f7a3cb | ||
|  | 9729492d1c | ||
|  | d6da8b4a96 | ||
|  | 9264cf4044 | ||
|  | 3a45c2a309 | ||
|  | 59de35c698 | ||
|  | 5b8ac2c809 | ||
|  | 83d0ff1c0a | ||
|  | 8a6ec6ceab | ||
|  | 93dbc74e33 | ||
|  | 5f2add48a9 | ||
|  | b7369875af | ||
|  | 2eb6580fed | ||
|  | 9f85fbb330 | ||
|  | ee9715a4cf | 
							
								
								
									
										7
									
								
								.devcontainer/.env.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.devcontainer/.env.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| COMPOSE_PROJECT_NAME=trmm | ||||
| IMAGE_REPO=tacticalrmm/ | ||||
| VERSION=latest | ||||
|  | ||||
| # DEV SETTINGS | ||||
| APP_PORT=443 | ||||
| DOCKER_NETWORK=172.21.0.0/24 | ||||
							
								
								
									
										26
									
								
								.devcontainer/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.devcontainer/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| version: '3.4' | ||||
|  | ||||
| services: | ||||
|   app-dev: | ||||
|     container_name: trmm-app-dev | ||||
|     image: node:16-alpine | ||||
|     restart: always | ||||
|     command: /bin/sh -c "npm install --cache ~/.npm && npm run serve" | ||||
|     user: 1000:1000 | ||||
|     working_dir: /workspace/web | ||||
|     volumes: | ||||
|       - ..:/workspace:cached | ||||
|     ports: | ||||
|       - "8080:443" | ||||
|     networks: | ||||
|       dev: | ||||
|         aliases: | ||||
|           - tactical-frontend | ||||
|  | ||||
| networks: | ||||
|   dev: | ||||
|     driver: bridge | ||||
|     ipam: | ||||
|       driver: default | ||||
|       config: | ||||
|         - subnet: ${DOCKER_NETWORK} | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -33,3 +33,4 @@ yarn-error.log* | ||||
| *.sln | ||||
|  | ||||
| .env | ||||
| /public/env-config.js | ||||
|   | ||||
							
								
								
									
										1472
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1472
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										32
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "web", | ||||
|   "version": "0.100.8-dev", | ||||
|   "version": "0.101.2-dev", | ||||
|   "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.15.0", | ||||
|     "apexcharts": "3.35.4", | ||||
|     "@quasar/extras": "1.15.5", | ||||
|     "apexcharts": "3.35.5", | ||||
|     "axios": "0.27.2", | ||||
|     "dotenv": "16.0.1", | ||||
|     "dotenv": "16.0.3", | ||||
|     "qrcode.vue": "3.3.3", | ||||
|     "quasar": "2.7.5", | ||||
|     "vue": "3.2.37", | ||||
|     "quasar": "2.10.0", | ||||
|     "vue": "3.2.41", | ||||
|     "vue3-ace-editor": "2.2.2", | ||||
|     "vue3-apexcharts": "1.4.1", | ||||
|     "vuedraggable": "4.1.0", | ||||
|     "vue-router": "4.1.2", | ||||
|     "vuex": "4.0.2" | ||||
|     "vue-router": "4.1.5", | ||||
|     "vuex": "4.1.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@quasar/cli": "^1.3.2", | ||||
|     "@intlify/vite-plugin-vue-i18n": "^5.0.1", | ||||
|     "@quasar/app-vite": "^1.0.5", | ||||
|     "@types/node": "^18.6.1", | ||||
|     "@typescript-eslint/eslint-plugin": "^5.30.5", | ||||
|     "@typescript-eslint/parser": "^5.30.5", | ||||
|     "autoprefixer": "^10.4.7", | ||||
|     "eslint": "^8.20.0", | ||||
|     "@intlify/vite-plugin-vue-i18n": "^6.0.3", | ||||
|     "@quasar/app-vite": "^1.1.3", | ||||
|     "@types/node": "^18.11.2", | ||||
|     "@typescript-eslint/eslint-plugin": "^5.40.1", | ||||
|     "@typescript-eslint/parser": "^5.40.1", | ||||
|     "autoprefixer": "^10.4.12", | ||||
|     "eslint": "^8.25.0", | ||||
|     "eslint-config-prettier": "^8.5.0", | ||||
|     "eslint-plugin-vue": "^8.5.0", | ||||
|     "prettier": "^2.7.1", | ||||
|     "typescript": "^4.7.4" | ||||
|     "typescript": "^4.8.4" | ||||
|   } | ||||
| } | ||||
| @@ -196,6 +196,14 @@ | ||||
|             > | ||||
|               <q-tooltip>Linux</q-tooltip> | ||||
|             </q-icon> | ||||
|             <q-icon | ||||
|               v-else-if="props.row.plat === 'darwin'" | ||||
|               name="mdi-apple" | ||||
|               size="sm" | ||||
|               color="primary" | ||||
|             > | ||||
|               <q-tooltip>macOS</q-tooltip> | ||||
|             </q-icon> | ||||
|           </q-td> | ||||
|  | ||||
|           <q-td key="checks-status" :props="props"> | ||||
| @@ -356,6 +364,27 @@ export default { | ||||
|   }, | ||||
|   methods: { | ||||
|     filterTable(rows, terms, cols, cellValue) { | ||||
|       const hiddenFields = [ | ||||
|         "version", | ||||
|         "operating_system", | ||||
|         "public_ip", | ||||
|         "cpu_model", | ||||
|         "graphics", | ||||
|         "local_ips", | ||||
|         "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, | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       const lowerTerms = terms ? terms.toLowerCase() : ""; | ||||
|       let advancedFilter = false; | ||||
|       let availability = null; | ||||
|   | ||||
| @@ -310,9 +310,10 @@ export default { | ||||
|     } | ||||
|  | ||||
|     function showUpdateDetails(update) { | ||||
|       const color = $q.dark.isActive ? "white" : ""; | ||||
|       let support_urls = ""; | ||||
|       update.more_info_urls.forEach((u) => { | ||||
|         support_urls += `<a href='${u}' target='_blank'>${u}</a><br/>`; | ||||
|         support_urls += `<a style='color: ${color}' href='${u}' target='_blank'>${u}</a><br/>`; | ||||
|       }); | ||||
|       let cats = update.categories.join(", "); | ||||
|       $q.dialog({ | ||||
|   | ||||
| @@ -7,6 +7,17 @@ | ||||
|           <q-badge color="primary" class="q-ml-sm text-caption">{{ | ||||
|             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> | ||||
|         </div> | ||||
|       </div> | ||||
|       <q-separator v-if="info.length > 1" /> | ||||
| @@ -15,6 +26,8 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { copyToClipboard } from "quasar"; | ||||
| import { notifySuccess } from "@/utils/notify"; | ||||
| // composition imports | ||||
| import { computed } from "vue"; | ||||
| import { useStore } from "vuex"; | ||||
| @@ -28,9 +41,17 @@ export default { | ||||
|     const store = useStore(); | ||||
|     const tabHeight = computed(() => store.state.tabHeight); | ||||
|  | ||||
|     function copyValueToClip(val) { | ||||
|       copyToClipboard(val) | ||||
|         .then(() => { | ||||
|           notifySuccess("Copied to clipboard"); | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       tabHeight, | ||||
|       uid, | ||||
|       copyValueToClip, | ||||
|     }; | ||||
|   }, | ||||
| }; | ||||
|   | ||||
| @@ -10,10 +10,13 @@ | ||||
|       </q-card-actions> | ||||
|     </q-card-section> | ||||
|     <q-card-section> | ||||
|       <p class="text-subtitle1"> | ||||
|       <p v-if="info.plat === 'windows'" class="text-subtitle1"> | ||||
|         Download the agent then run the following command from an elevated | ||||
|         command prompt on the device you want to add. | ||||
|       </p> | ||||
|       <p v-else-if="info.plat === 'darwin'" class="text-subtitle1"> | ||||
|         Run the following command from a terminal | ||||
|       </p> | ||||
|       <p> | ||||
|         <q-field outlined :color="$q.dark.isActive ? 'white' : 'black'"> | ||||
|           <code>{{ info.data.cmd }}</code> | ||||
| @@ -37,7 +40,7 @@ | ||||
|           </q-badge> | ||||
|           <span>Do not popup any message boxes during install</span> | ||||
|         </div> | ||||
|         <div class="q-pa-xs q-gutter-xs"> | ||||
|         <div v-if="info.plat === 'windows'" class="q-pa-xs q-gutter-xs"> | ||||
|           <q-badge class="text-caption q-mr-xs" color="grey" text-color="black"> | ||||
|             <code | ||||
|               >-local-mesh "C:\\<some folder or | ||||
| @@ -46,7 +49,7 @@ | ||||
|           </q-badge> | ||||
|           <span> To skip downloading the Mesh Agent during the install.</span> | ||||
|         </div> | ||||
|         <div class="q-pa-xs q-gutter-xs"> | ||||
|         <div v-if="info.plat === 'windows'" class="q-pa-xs q-gutter-xs"> | ||||
|           <q-badge class="text-caption q-mr-xs" color="grey" text-color="black"> | ||||
|             <code | ||||
|               >-meshdir "C:\Program Files\Your Company Name\Mesh Agent"</code | ||||
| @@ -63,7 +66,7 @@ | ||||
|           </q-badge> | ||||
|           <span>Don't install the mesh agent</span> | ||||
|         </div> | ||||
|         <div class="q-pa-xs q-gutter-xs"> | ||||
|         <div v-if="info.plat === 'windows'" class="q-pa-xs q-gutter-xs"> | ||||
|           <q-badge class="text-caption q-mr-xs" color="grey" text-color="black"> | ||||
|             <code>-cert "C:\\<some folder or path>\\ca.pem"</code> | ||||
|           </q-badge> | ||||
| @@ -86,12 +89,12 @@ | ||||
|       <p class="text-italic"> | ||||
|         Note: the auth token above will be valid for {{ info.expires }} hours. | ||||
|       </p> | ||||
|       <q-btn | ||||
|       <q-btn v-if="info.plat === 'windows'" | ||||
|         type="a" | ||||
|         :href="info.data.url" | ||||
|         color="primary" | ||||
|         label="Download Agent" | ||||
|       /> | ||||
|       ></q-btn> | ||||
|     </q-card-section> | ||||
|   </q-card> | ||||
| </template> | ||||
|   | ||||
| @@ -223,6 +223,7 @@ const monTypeOptions = [ | ||||
| const osTypeOptions = [ | ||||
|   { label: "Windows", value: "windows" }, | ||||
|   { label: "Linux", value: "linux" }, | ||||
|   { label: "macOS", value: "darwin" }, | ||||
|   { label: "All", value: "all" }, | ||||
| ]; | ||||
|  | ||||
|   | ||||
| @@ -465,8 +465,51 @@ export default { | ||||
|       }); | ||||
|     }, | ||||
|     editAgent() { | ||||
|       delete this.agent.all_timezones; | ||||
|       delete this.agent.timezone; | ||||
|       // TODO we need to fix the serializer to not send this stuff | ||||
|       const toRemove = [ | ||||
|         "created_by", | ||||
|         "created_time", | ||||
|         "modified_by", | ||||
|         "modified_time", | ||||
|         "all_timezones", | ||||
|         "timezone", | ||||
|         "wmi_detail", | ||||
|         "services", | ||||
|         "status", | ||||
|         "cpu_model", | ||||
|         "local_ips", | ||||
|         "make_model", | ||||
|         "physical_disks", | ||||
|         "graphics", | ||||
|         "checks", | ||||
|         "patches_last_installed", | ||||
|         "last_seen", | ||||
|         "applied_policies", | ||||
|         "effective_patch_policy", | ||||
|         "version", | ||||
|         "operating_system", | ||||
|         "plat", | ||||
|         "goarch", | ||||
|         "hostname", | ||||
|         "public_ip", | ||||
|         "total_ram", | ||||
|         "disks", | ||||
|         "boot_time", | ||||
|         "logged_in_username", | ||||
|         "last_logged_in_user", | ||||
|         "needs_reboot", | ||||
|         "choco_installed", | ||||
|         "policy", | ||||
|         "mesh_node_id", | ||||
|         "block_policy_inheritance", | ||||
|         "maintenance_mode", | ||||
|         "alert_template", | ||||
|         "client", | ||||
|         "site_name", | ||||
|       ]; | ||||
|       for (const elem of toRemove) { | ||||
|         delete this.agent[elem]; | ||||
|       } | ||||
|  | ||||
|       // only send the timezone data if it has changed | ||||
|       // this way django will keep the db column as null and inherit from the global setting | ||||
| @@ -503,7 +546,7 @@ export default { | ||||
|         else if (day === 0) result += "Sun, "; | ||||
|       } | ||||
|  | ||||
|       return result.trimRight(","); | ||||
|       return result.trimEnd(","); | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|   | ||||
| @@ -52,6 +52,15 @@ | ||||
|                 goarch = GOARCH_AMD64; | ||||
|               " | ||||
|             /> | ||||
|             <q-radio | ||||
|               v-model="agentOS" | ||||
|               val="darwin" | ||||
|               label="macOS" | ||||
|               @update:model-value=" | ||||
|                 installMethod = 'mac'; | ||||
|                 goarch = GOARCH_AMD64; | ||||
|               " | ||||
|             /> | ||||
|           </div> | ||||
|         </q-card-section> | ||||
|         <q-card-section> | ||||
| @@ -105,37 +114,37 @@ | ||||
|               v-model="goarch" | ||||
|               :val="GOARCH_AMD64" | ||||
|               label="64 bit" | ||||
|               v-show="agentOS === 'windows'" | ||||
|             /> | ||||
|             <q-radio | ||||
|               v-model="goarch" | ||||
|               :val="GOARCH_i386" | ||||
|               label="32 bit" | ||||
|               v-show="agentOS === 'windows'" | ||||
|               v-show="agentOS === 'windows' || agentOS === 'linux'" | ||||
|             /> | ||||
|             <q-radio | ||||
|               v-model="goarch" | ||||
|               :val="GOARCH_AMD64" | ||||
|               label="64 bit" | ||||
|               v-show="agentOS !== 'windows'" | ||||
|               label="Intel 64 bit" | ||||
|               v-show="agentOS === 'darwin'" | ||||
|             /> | ||||
|             <q-radio | ||||
|               v-model="goarch" | ||||
|               :val="GOARCH_i386" | ||||
|               label="32 bit" | ||||
|               v-show="agentOS !== 'windows'" | ||||
|               v-show="agentOS !== 'darwin'" | ||||
|             /> | ||||
|             <q-radio | ||||
|               v-model="goarch" | ||||
|               :val="GOARCH_ARM64" | ||||
|               label="ARM 64 bit" | ||||
|               v-show="agentOS !== 'windows'" | ||||
|               v-show="agentOS === 'linux'" | ||||
|             /> | ||||
|             <q-radio | ||||
|               v-model="goarch" | ||||
|               :val="GOARCH_ARM64" | ||||
|               label="Apple Silicon (M1, M2)" | ||||
|               v-show="agentOS === 'darwin'" | ||||
|             /> | ||||
|             <q-radio | ||||
|               v-model="goarch" | ||||
|               :val="GOARCH_ARM32" | ||||
|               label="ARM 32 bit (Rasp Pi)" | ||||
|               v-show="agentOS !== 'windows'" | ||||
|               v-show="agentOS === 'linux'" | ||||
|             /> | ||||
|           </div> | ||||
|         </q-card-section> | ||||
| @@ -266,12 +275,13 @@ export default { | ||||
|         plat: this.agentOS, | ||||
|       }; | ||||
|  | ||||
|       if (this.installMethod === "manual") { | ||||
|       if (this.installMethod === "manual" || this.installMethod === "mac") { | ||||
|         this.$axios.post("/agents/installer/", data).then((r) => { | ||||
|           this.info = { | ||||
|             expires: this.expires, | ||||
|             data: r.data, | ||||
|             goarch: this.goarch, | ||||
|             plat: this.agentOS, | ||||
|           }; | ||||
|           this.showAgentDownload = true; | ||||
|         }); | ||||
| @@ -343,6 +353,9 @@ export default { | ||||
|         case "bash": | ||||
|           text = "Download linux install script"; | ||||
|           break; | ||||
|         case "mac": | ||||
|           text = "Show installation instructions"; | ||||
|           break; | ||||
|       } | ||||
|  | ||||
|       return text; | ||||
|   | ||||
| @@ -37,4 +37,5 @@ export function cmdPlaceholder(shell) { | ||||
| export const agentPlatformOptions = [ | ||||
|   { value: "windows", label: "Windows" }, | ||||
|   { value: "linux", label: "Linux" }, | ||||
|   { value: "darwin", label: "macOS" }, | ||||
| ]; | ||||
|   | ||||
| @@ -14,6 +14,22 @@ | ||||
|           @click="$store.dispatch('reload')" | ||||
|         /> | ||||
|       </q-banner> | ||||
|       <q-banner | ||||
|         v-if="!hosted && tokenExpired" | ||||
|         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-btn | ||||
|           color="dark" | ||||
|           icon="refresh" | ||||
|           label="Refresh" | ||||
|           @click="$store.dispatch('reload')" | ||||
|         /> | ||||
|       </q-banner> | ||||
|       <q-toolbar> | ||||
|         <q-btn | ||||
|           dense | ||||
| @@ -167,6 +183,7 @@ export default { | ||||
|     const needRefresh = computed(() => store.state.needrefresh); | ||||
|     const user = computed(() => store.state.username); | ||||
|     const hosted = computed(() => store.state.hosted); | ||||
|     const tokenExpired = computed(() => store.state.tokenExpired); | ||||
|  | ||||
|     const latestReleaseURL = computed(() => { | ||||
|       return latestTRMMVersion.value | ||||
| @@ -259,6 +276,8 @@ export default { | ||||
|       user, | ||||
|       needRefresh, | ||||
|       darkMode, | ||||
|       hosted, | ||||
|       tokenExpired, | ||||
|  | ||||
|       // methods | ||||
|       showUserPreferences, | ||||
|   | ||||
| @@ -17,6 +17,7 @@ export default function () { | ||||
|         agentPlatform: "windows", | ||||
|         agentTableLoading: false, | ||||
|         needrefresh: false, | ||||
|         tokenExpired: false, | ||||
|         refreshSummaryTab: false, | ||||
|         tableHeight: "300px", | ||||
|         tabHeight: "300px", | ||||
| @@ -83,6 +84,9 @@ export default function () { | ||||
|       SET_REFRESH_NEEDED(state, action) { | ||||
|         state.needrefresh = action; | ||||
|       }, | ||||
|       SET_TOKEN_EXPIRED(state, action) { | ||||
|         state.tokenExpired = action; | ||||
|       }, | ||||
|       SET_SPLITTER(state, val) { | ||||
|         // top toolbar is 50px. Filebar is 40px and agent filter tabs are 44px | ||||
|         state.tableHeight = `${Screen.height - 50 - 40 - 78 - val}px`; | ||||
| @@ -212,6 +216,7 @@ export default function () { | ||||
|         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); | ||||
|  | ||||
|         if (data.date_format && data.date_format !== "") | ||||
|           context.commit("setDateFormat", data.date_format); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user