Compare commits

...

63 Commits

Author SHA1 Message Date
wh1te909
8207f30234 Release 0.101.29 2023-08-30 04:10:11 +00:00
wh1te909
1879977b83 bump version [skip ci] 2023-08-30 04:09:53 +00:00
wh1te909
b4de579a74 fix lint 2023-08-29 20:42:23 +00:00
wh1te909
23f15ff9e5 feat: add reset all check status amidaware/tacticalrmm#1615 2023-08-29 20:21:22 +00:00
wh1te909
498e038bbb update reqs 2023-08-29 20:19:35 +00:00
wh1te909
bb1f1c19cf wording 2023-08-21 21:30:21 +00:00
Dan
74e3aa4e46 Merge pull request #12 from silversword411/develop
Fixing main view removing blank space along bottom
2023-08-21 14:28:28 -07:00
silversword411
07a8e3ebcb Clarifying TRMM agent 2023-08-21 09:28:23 -04:00
silversword411
89966dd006 Fixing main view removing blank space along bottom 2023-08-18 02:39:43 -04:00
wh1te909
68036f6837 Release 0.101.28 2023-08-14 06:39:49 +00:00
wh1te909
45ac82b1dd bump version 2023-08-14 06:39:31 +00:00
wh1te909
d94e5c7965 update reqs 2023-08-11 07:07:00 +00:00
wh1te909
e0c1b3199a update reqs 2023-08-06 23:01:46 +00:00
sadnub
fdbbdf7394 make it so the script manager doesn't close if escape is pressed on script editor 2023-08-05 09:54:14 -04:00
wh1te909
03fae45ac5 Release 0.101.25 2023-07-04 18:49:46 +00:00
wh1te909
346670e8ea bump version 2023-07-04 18:49:28 +00:00
wh1te909
e030efaecf bump dev ver 2023-07-03 23:32:40 +00:00
wh1te909
b8a4f9fe74 update reqs 2023-07-03 23:32:09 +00:00
wh1te909
f963b51d70 update quasar 2023-07-01 16:48:03 +00:00
wh1te909
feacb19cf9 bump dev version 2023-06-30 20:30:52 +00:00
wh1te909
7ce2c1e969 node 18 2023-06-30 20:26:30 +00:00
wh1te909
d1defcef4a update reqs 2023-06-30 20:26:21 +00:00
Dan
e674b4fa5d Merge pull request #9 from silversword411/develop
Adding customizable columns to script manager table
2023-06-07 11:59:29 -07:00
sadnub
b08a5a6c2d fix light mode colors 2023-06-06 16:17:09 -04:00
sadnub
9fa1d7209f fix duplicate columns on script manager close and open 2023-06-06 15:42:06 -04:00
Dan
2adfccfa1d Merge pull request #10 from dinger1986/develop
Update InitialSetup.vue
2023-06-06 11:51:01 -07:00
silversword411
04766efcd0 tactical-table and script manager table for column selection 2023-06-06 12:38:53 -04:00
dinger1986
4babb937f6 Update InitialSetup.vue 2023-06-01 21:37:06 +00:00
wh1te909
c2591c9e7d Release 0.101.22 2023-05-30 22:11:30 +00:00
wh1te909
69403def2a bump version 2023-05-30 22:11:11 +00:00
wh1te909
3fdd8272f6 bump version 2023-05-29 20:36:02 +00:00
wh1te909
339227bedc add cert expiring soon indicator closes amidaware/tacticalrmm#722 2023-05-27 23:31:07 +00:00
wh1te909
17c7c95cc1 group processors for cleaner UI closes amidaware/tacticalrmm#583 2023-05-27 20:05:53 +00:00
wh1te909
a3ceb5e81b add serial number to summary tab closes amidaware/tacticalrmm#389 2023-05-27 19:48:19 +00:00
wh1te909
679d8cab77 add wake-on-lan amidaware/tacticalrmm#1180 2023-05-27 00:38:34 +00:00
wh1te909
c4c1474e09 add serial number to search amidaware/tacticalrmm#1355 2023-05-26 22:54:46 +00:00
wh1te909
82677b0b82 make cmd placeholder text customizable closes #5 2023-05-26 22:18:01 +00:00
wh1te909
b78af07f11 allow customizing dashboard colors closes amidaware/tacticalrmm#1514 2023-05-25 22:31:16 +00:00
wh1te909
24acef19c5 formatting 2023-05-25 22:30:05 +00:00
wh1te909
fee6edb39e update reqs 2023-05-25 22:21:34 +00:00
wh1te909
89e7db905d fix client sorting fixes amidaware/tacticalrmm#1439 2023-05-25 21:27:45 +00:00
wh1te909
827e81dcda don't retry null token fixes amidaware/tacticalrmm#1199 2023-05-17 07:03:13 +00:00
wh1te909
6ea3a053f2 update reqs 2023-05-17 07:00:38 +00:00
sadnub
88d297f7c6 Change the default color of anchor tags to make them look better in dark mode 2023-05-13 00:00:36 -04:00
sadnub
6c57d3e6b1 Make the alert icon menu height fixed 2023-05-12 23:59:18 -04:00
wh1te909
7fcbe6fbd8 Release 0.101.20 2023-05-09 21:09:45 +00:00
wh1te909
0113fbc761 hide openai until next release 2023-05-09 21:06:40 +00:00
wh1te909
a2f472ef9c Release 0.101.18 2023-04-09 03:28:23 +00:00
wh1te909
8403ac0e93 Release 0.101.16 2023-03-22 17:00:29 +00:00
wh1te909
b7a91563b0 Release 0.101.13 2023-01-18 20:05:20 +00:00
wh1te909
ab19afca16 Release 0.101.11 2022-12-21 18:44:46 +00:00
wh1te909
f24c6a7a80 Release 0.101.9 2022-12-04 23:01:59 +00:00
wh1te909
99490bf859 Release 0.101.7 2022-11-13 01:20:33 +00:00
wh1te909
72cdeeaa6a Release 0.101.5 2022-10-25 22:02:34 +00:00
wh1te909
1eca4d605b Release 0.101.3 2022-10-19 22:35:54 +00:00
wh1te909
52ee98f6f8 Release 0.101.0 2022-09-24 02:43:53 +00:00
wh1te909
d270b877c9 Release 0.100.9 2022-08-23 05:04:57 +00:00
wh1te909
fd8b2a1d98 Release 0.100.8 2022-08-09 20:40:48 +00:00
wh1te909
f518043d8d Release 0.100.7 2022-08-01 17:36:11 +00:00
wh1te909
cc2335558d Release 0.100.6 2022-07-27 06:15:49 +00:00
wh1te909
a8a171ba2c Release 0.100.5 2022-07-10 00:00:08 +00:00
wh1te909
24a63f477e Release 0.100.4 2022-07-07 16:38:14 +00:00
wh1te909
ddeb6293a1 init 2022-05-17 20:46:22 +00:00
30 changed files with 1578 additions and 1203 deletions

View File

@@ -15,7 +15,7 @@ jobs:
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 16 node-version: 18
- run: touch env-config.js - run: touch env-config.js
@@ -32,4 +32,3 @@ jobs:
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: trmm-web-${{github.ref_name}}.tar.gz files: trmm-web-${{github.ref_name}}.tar.gz

1823
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "web", "name": "web",
"version": "0.101.19-dev", "version": "0.101.29",
"private": true, "private": true,
"productName": "Tactical RMM", "productName": "Tactical RMM",
"scripts": { "scripts": {
@@ -10,31 +10,31 @@
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore" "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "1.16.3", "@quasar/extras": "1.16.6",
"apexcharts": "3.40.0", "apexcharts": "3.41.1",
"axios": "1.4.0", "axios": "1.4.0",
"dotenv": "16.0.3", "dotenv": "16.3.1",
"qrcode.vue": "3.3.4", "qrcode.vue": "3.4.1",
"quasar": "2.12.0", "quasar": "2.12.5",
"vue": "3.2.47", "vue": "3.3.4",
"vue3-ace-editor": "2.2.2", "vue3-ace-editor": "2.2.3",
"vue3-apexcharts": "1.4.1", "vue3-apexcharts": "1.4.4",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
"vue-router": "4.1.6", "vue-router": "4.2.4",
"vuex": "4.1.0" "vuex": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@quasar/cli": "^2.1.0", "@quasar/cli": "2.2.3",
"@intlify/unplugin-vue-i18n": "^0.10.0", "@intlify/unplugin-vue-i18n": "0.12.3",
"@quasar/app-vite": "^1.3.0", "@quasar/app-vite": "1.4.6",
"@types/node": "^18.16.5", "@types/node": "20.5.7",
"@typescript-eslint/eslint-plugin": "^5.59.2", "@typescript-eslint/eslint-plugin": "6.5.0",
"@typescript-eslint/parser": "^5.59.2", "@typescript-eslint/parser": "6.5.0",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.15",
"eslint": "8.40.0", "eslint": "8.48.0",
"eslint-config-prettier": "8.8.0", "eslint-config-prettier": "9.0.0",
"eslint-plugin-vue": "8.7.1", "eslint-plugin-vue": "8.7.1",
"prettier": "2.8.8", "prettier": "3.0.3",
"typescript": "5.0.4" "typescript": "5.2.2"
} }
} }

View File

@@ -12,6 +12,9 @@ export default {
body body
overflow-y: hidden overflow-y: hidden
a
color: #1976D2
.tbl-sticky .tbl-sticky
thead tr th thead tr th
position: sticky position: sticky

View File

@@ -232,3 +232,8 @@ export async function removeAgentNote(pk) {
const { data } = await axios.delete(`${baseUrl}/notes/${pk}/`); const { data } = await axios.delete(`${baseUrl}/notes/${pk}/`);
return data; return data;
} }
export async function wakeUpWOL(agent_id) {
const { data } = await axios.post(`${baseUrl}/${agent_id}/wol/`);
return data;
}

View File

@@ -31,6 +31,11 @@ export async function resetCheck(id) {
return data; 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) { export async function runAgentChecks(agent_id) {
const { data } = await axios.post(`${baseUrl}/${agent_id}/run/`); const { data } = await axios.post(`${baseUrl}/${agent_id}/run/`);
return data; return data;

View File

@@ -211,7 +211,7 @@
v-if="props.row.maintenance_mode" v-if="props.row.maintenance_mode"
name="construction" name="construction"
size="1.2em" size="1.2em"
color="green" :color="dash_positive_color"
> >
<q-tooltip>Maintenance Mode Enabled</q-tooltip> <q-tooltip>Maintenance Mode Enabled</q-tooltip>
</q-icon> </q-icon>
@@ -219,7 +219,7 @@
v-else-if="props.row.checks.failing > 0" v-else-if="props.row.checks.failing > 0"
name="fas fa-check-double" name="fas fa-check-double"
size="1.2em" size="1.2em"
color="negative" :color="dash_negative_color"
> >
<q-tooltip>Checks failing</q-tooltip> <q-tooltip>Checks failing</q-tooltip>
</q-icon> </q-icon>
@@ -227,7 +227,7 @@
v-else-if="props.row.checks.warning > 0" v-else-if="props.row.checks.warning > 0"
name="fas fa-check-double" name="fas fa-check-double"
size="1.2em" size="1.2em"
color="warning" :color="dash_warning_color"
> >
<q-tooltip>Checks warning</q-tooltip> <q-tooltip>Checks warning</q-tooltip>
</q-icon> </q-icon>
@@ -235,7 +235,7 @@
v-else-if="props.row.checks.info > 0" v-else-if="props.row.checks.info > 0"
name="fas fa-check-double" name="fas fa-check-double"
size="1.2em" size="1.2em"
color="info" :color="dash_info_color"
> >
<q-tooltip>Checks info</q-tooltip> <q-tooltip>Checks info</q-tooltip>
</q-icon> </q-icon>
@@ -243,7 +243,7 @@
v-else v-else
name="fas fa-check-double" name="fas fa-check-double"
size="1.2em" size="1.2em"
color="positive" :color="dash_positive_color"
> >
<q-tooltip>Checks passing</q-tooltip> <q-tooltip>Checks passing</q-tooltip>
</q-icon> </q-icon>
@@ -279,7 +279,7 @@
@click="showPendingActionsModal(props.row)" @click="showPendingActionsModal(props.row)"
name="far fa-clock" name="far fa-clock"
size="1.4em" size="1.4em"
color="warning" :color="dash_warning_color"
class="cursor-pointer" class="cursor-pointer"
> >
<q-tooltip <q-tooltip
@@ -303,7 +303,7 @@
v-if="props.row.status === 'overdue'" v-if="props.row.status === 'overdue'"
name="fas fa-signal" name="fas fa-signal"
size="1.2em" size="1.2em"
color="negative" :color="dash_negative_color"
> >
<q-tooltip>Agent overdue</q-tooltip> <q-tooltip>Agent overdue</q-tooltip>
</q-icon> </q-icon>
@@ -311,11 +311,16 @@
v-else-if="props.row.status === 'offline'" v-else-if="props.row.status === 'offline'"
name="fas fa-signal" name="fas fa-signal"
size="1.2em" size="1.2em"
color="warning" :color="dash_warning_color"
> >
<q-tooltip>Agent offline</q-tooltip> <q-tooltip>Agent offline</q-tooltip>
</q-icon> </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-tooltip>Agent online</q-tooltip>
</q-icon> </q-icon>
</q-td> </q-td>
@@ -373,7 +378,8 @@ export default {
"local_ips", "local_ips",
"make_model", "make_model",
"physical_disks", "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 // 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 // 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) => { return allColumns.some((col) => {
let valObj = cellValue(col, row); let valObj = cellValue(col, row);
if (Array.isArray(valObj)) { 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 val = valObj + "";
const haystack = const haystack =
@@ -488,7 +494,9 @@ export default {
const data = { const data = {
[db_field]: !alert_action, [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.$axios.put(`/agents/${agent.agent_id}/`, data).then(() => {
this.$q.notify({ this.$q.notify({
color: alertColor, color: alertColor,
@@ -532,7 +540,13 @@ export default {
}, },
}, },
computed: { computed: {
...mapState(["tableHeight"]), ...mapState([
"tableHeight",
"dash_info_color",
"dash_positive_color",
"dash_negative_color",
"dash_warning_color",
]),
agentDblClickAction() { agentDblClickAction() {
return this.$store.state.agentDblClickAction; return this.$store.state.agentDblClickAction;
}, },

View File

@@ -3,7 +3,7 @@
<q-badge v-if="alertsCount > 0" :color="badgeColor" floating transparent>{{ <q-badge v-if="alertsCount > 0" :color="badgeColor" floating transparent>{{
alertsCountText() alertsCountText()
}}</q-badge> }}</q-badge>
<q-menu style="max-height: 30vh"> <q-menu :style="{ 'max-height': `${$q.screen.height - 100}px` }">
<q-list separator> <q-list separator>
<q-item v-if="alertsCount === 0">No New Alerts</q-item> <q-item v-if="alertsCount === 0">No New Alerts</q-item>
<q-item v-for="alert in topAlerts" :key="alert.id"> <q-item v-for="alert in topAlerts" :key="alert.id">
@@ -59,6 +59,7 @@
</template> </template>
<script> <script>
import { mapState } from "vuex";
import mixins from "@/mixins/mixins"; import mixins from "@/mixins/mixins";
import AlertsOverview from "@/components/modals/alerts/AlertsOverview.vue"; import AlertsOverview from "@/components/modals/alerts/AlertsOverview.vue";
import { getTimeLapse } from "@/utils/format"; import { getTimeLapse } from "@/utils/format";
@@ -75,19 +76,21 @@ export default {
return { return {
alertsCount: 0, alertsCount: 0,
topAlerts: [], topAlerts: [],
errorColor: "red",
warningColor: "orange",
infoColor: "blue",
poll: null, poll: null,
}; };
}, },
computed: { computed: {
...mapState([
"dash_info_color",
"dash_warning_color",
"dash_negative_color",
]),
badgeColor() { badgeColor() {
const severities = this.topAlerts.map((alert) => alert.severity); const severities = this.topAlerts.map((alert) => alert.severity);
if (severities.includes("error")) return this.errorColor; if (severities.includes("error")) return this.dash_negative_color;
else if (severities.includes("warning")) return this.warningColor; else if (severities.includes("warning")) return this.dash_warning_color;
else return this.infoColor; else return this.dash_info_color;
}, },
}, },
methods: { methods: {
@@ -159,9 +162,9 @@ export default {
}); });
}, },
alertIconColor(severity) { alertIconColor(severity) {
if (severity === "error") return this.errorColor; if (severity === "error") return this.dash_negative_color;
else if (severity === "warning") return this.warningColor; else if (severity === "warning") return this.dash_warning_color;
else return this.infoColor; else return this.dash_info_color;
}, },
alertsCountText() { alertsCountText() {
if (this.alertsCount > 99) return "99+"; if (this.alertsCount > 99) return "99+";

View File

@@ -98,6 +98,10 @@
v-model="localRole.can_reboot_agents" v-model="localRole.can_reboot_agents"
label="Reboot Agents" label="Reboot Agents"
/> />
<q-checkbox
v-model="localRole.can_send_wol"
label="Wake-Up (WoL) Agents"
/>
<q-checkbox <q-checkbox
v-model="localRole.can_install_agents" v-model="localRole.can_install_agents"
label="Install Agents" label="Install Agents"
@@ -437,8 +441,8 @@ export default {
can_run_scripts: false, can_run_scripts: false,
can_run_bulk: false, can_run_bulk: false,
can_manage_winsvcs: false, can_manage_winsvcs: false,
can_recover_agents: false,
can_list_agent_history: false, can_list_agent_history: false,
can_send_wol: false,
// software perms // software perms
can_list_software: false, can_list_software: false,
can_manage_software: false, can_manage_software: false,

View File

@@ -146,6 +146,13 @@
<q-item-section>Run Checks</q-item-section> <q-item-section>Run Checks</q-item-section>
</q-item> </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 clickable>
<q-item-section side> <q-item-section side>
<q-icon size="xs" name="power_settings_new" /> <q-icon size="xs" name="power_settings_new" />
@@ -210,6 +217,7 @@ import {
removeAgent, removeAgent,
runRemoteBackground, runRemoteBackground,
runTakeControl, runTakeControl,
wakeUpWOL,
} from "@/api/agents"; } from "@/api/agents";
import { runAgentUpdateScan, runAgentUpdateInstall } from "@/api/winupdates"; import { runAgentUpdateScan, runAgentUpdateInstall } from "@/api/winupdates";
import { runAgentChecks } from "@/api/checks"; 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) { function showRebootLaterModal(agent) {
$q.dialog({ $q.dialog({
component: RebootLater, component: RebootLater,
@@ -498,6 +515,7 @@ export default {
showPolicyAdd, showPolicyAdd,
showAgentRecovery, showAgentRecovery,
pingAgent, pingAgent,
wakeUp,
}; };
}, },
}; };

View File

@@ -261,7 +261,7 @@
<q-td v-else-if="props.row.task_result.status === 'passing'"> <q-td v-else-if="props.row.task_result.status === 'passing'">
<q-icon <q-icon
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="positive" :color="dash_positive_color"
name="check_circle" name="check_circle"
> >
<q-tooltip>Passing</q-tooltip> <q-tooltip>Passing</q-tooltip>
@@ -271,7 +271,7 @@
<q-icon <q-icon
v-if="props.row.alert_severity === 'info'" v-if="props.row.alert_severity === 'info'"
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="info" :color="dash_info_color"
name="info" name="info"
> >
<q-tooltip>Informational</q-tooltip> <q-tooltip>Informational</q-tooltip>
@@ -279,7 +279,7 @@
<q-icon <q-icon
v-else-if="props.row.alert_severity === 'warning'" v-else-if="props.row.alert_severity === 'warning'"
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="warning" :color="dash_warning_color"
name="warning" name="warning"
> >
<q-tooltip>Warning</q-tooltip> <q-tooltip>Warning</q-tooltip>
@@ -287,7 +287,7 @@
<q-icon <q-icon
v-else v-else
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="negative" :color="dash_negative_color"
name="error" name="error"
> >
<q-tooltip>Error</q-tooltip> <q-tooltip>Error</q-tooltip>
@@ -418,6 +418,10 @@ export default {
const tabHeight = computed(() => store.state.tabHeight); const tabHeight = computed(() => store.state.tabHeight);
const agentPlatform = computed(() => store.state.agentPlatform); const agentPlatform = computed(() => store.state.agentPlatform);
const formatDate = computed(() => store.getters.formatDate); 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 // setup quasar
const $q = useQuasar(); const $q = useQuasar();
@@ -552,6 +556,10 @@ export default {
selectedAgent, selectedAgent,
tabHeight, tabHeight,
agentPlatform, agentPlatform,
dash_info_color,
dash_positive_color,
dash_warning_color,
dash_negative_color,
// non-reactive data // non-reactive data
columns, columns,

View File

@@ -119,6 +119,16 @@
no-caps no-caps
icon="play_arrow" icon="play_arrow"
@click="runChecks" @click="runChecks"
class="q-mr-md"
/>
<q-btn
label="Reset All Checks Status"
dense
flat
push
no-caps
icon="restart_alt"
@click="resetAllChecks"
/> />
</template> </template>
@@ -301,7 +311,7 @@
<q-td v-else-if="props.row.check_result.status === 'passing'"> <q-td v-else-if="props.row.check_result.status === 'passing'">
<q-icon <q-icon
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="positive" :color="dash_positive_color"
name="check_circle" name="check_circle"
> >
<q-tooltip>Passing</q-tooltip> <q-tooltip>Passing</q-tooltip>
@@ -311,7 +321,7 @@
<q-icon <q-icon
v-if="getAlertSeverity(props.row) === 'info'" v-if="getAlertSeverity(props.row) === 'info'"
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="info" :color="dash_info_color"
name="info" name="info"
> >
<q-tooltip>Informational</q-tooltip> <q-tooltip>Informational</q-tooltip>
@@ -319,7 +329,7 @@
<q-icon <q-icon
v-else-if="getAlertSeverity(props.row) === 'warning'" v-else-if="getAlertSeverity(props.row) === 'warning'"
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="warning" :color="dash_warning_color"
name="warning" name="warning"
> >
<q-tooltip>Warning</q-tooltip> <q-tooltip>Warning</q-tooltip>
@@ -327,7 +337,7 @@
<q-icon <q-icon
v-else v-else
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="negative" :color="dash_negative_color"
name="error" name="error"
> >
<q-tooltip>Error</q-tooltip> <q-tooltip>Error</q-tooltip>
@@ -415,6 +425,7 @@ import {
updateCheck, updateCheck,
removeCheck, removeCheck,
resetCheck, resetCheck,
resetAllChecksStatus,
runAgentChecks, runAgentChecks,
} from "@/api/checks"; } from "@/api/checks";
import { fetchAgentChecks } from "@/api/agents"; import { fetchAgentChecks } from "@/api/agents";
@@ -479,6 +490,10 @@ export default {
const tabHeight = computed(() => store.state.tabHeight); const tabHeight = computed(() => store.state.tabHeight);
const agentPlatform = computed(() => store.state.agentPlatform); const agentPlatform = computed(() => store.state.agentPlatform);
const formatDate = computed(() => store.getters.formatDate); 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 // setup quasar
const $q = useQuasar(); const $q = useQuasar();
@@ -568,7 +583,7 @@ export default {
notifySuccess(result); notifySuccess(result);
refreshDashboard( refreshDashboard(
false /* clearTreeSelected */, false /* clearTreeSelected */,
false /* clearSubTable */ false /* clearSubTable */,
); );
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@@ -576,6 +591,27 @@ export default {
loading.value = false; 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) { function showEventInfo(data) {
$q.dialog({ $q.dialog({
component: EventLogCheckOutput, component: EventLogCheckOutput,
@@ -653,6 +689,10 @@ export default {
tabHeight, tabHeight,
selectedAgent, selectedAgent,
agentPlatform, agentPlatform,
dash_info_color,
dash_positive_color,
dash_warning_color,
dash_negative_color,
// non-reactive data // non-reactive data
columns, columns,
@@ -666,6 +706,7 @@ export default {
formatDate, formatDate,
getAlertSeverity, getAlertSeverity,
runChecks, runChecks,
resetAllChecks,
// dialogs // dialogs
showScriptOutput, showScriptOutput,

View File

@@ -18,6 +18,33 @@
icon="refresh" icon="refresh"
@click="refreshSummary" @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> <b>{{ summary.hostname }}</b>
<span v-if="summary.maintenance_mode"> <span v-if="summary.maintenance_mode">
&bull; <q-badge color="green"> Maintenance Mode </q-badge> &bull; <q-badge color="green"> Maintenance Mode </q-badge>
@@ -60,7 +87,7 @@
</q-item-section> </q-item-section>
<q-item-section>{{ summary.make_model }}</q-item-section> <q-item-section>{{ summary.make_model }}</q-item-section>
</q-item> </q-item>
<q-item v-for="(cpu, i) in summary.cpu_model" :key="cpu + i"> <q-item>
<q-item-section avatar> <q-item-section avatar>
<q-icon name="fas fa-microchip" /> <q-icon name="fas fa-microchip" />
</q-item-section> </q-item-section>
@@ -87,6 +114,13 @@
</q-item-section> </q-item-section>
<q-item-section>{{ summary.graphics }}</q-item-section> <q-item-section>{{ summary.graphics }}</q-item-section>
</q-item> </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>
<q-item-section avatar> <q-item-section avatar>
<q-icon name="fas fa-globe-americas" /> <q-icon name="fas fa-globe-americas" />
@@ -110,7 +144,7 @@
size="lg" size="lg"
square square
icon="done" icon="done"
color="green" :color="dash_positive_color"
text-color="white" text-color="white"
/> />
<small>{{ summary.checks.passing }} checks passing</small> <small>{{ summary.checks.passing }} checks passing</small>
@@ -120,7 +154,7 @@
size="lg" size="lg"
square square
icon="cancel" icon="cancel"
color="red" :color="dash_negative_color"
text-color="white" text-color="white"
/> />
<small>{{ summary.checks.failing }} checks failing</small> <small>{{ summary.checks.failing }} checks failing</small>
@@ -130,7 +164,7 @@
size="lg" size="lg"
square square
icon="warning" icon="warning"
color="warning" :color="dash_warning_color"
text-color="white" text-color="white"
/> />
<small>{{ summary.checks.warning }} checks warning</small> <small>{{ summary.checks.warning }} checks warning</small>
@@ -140,7 +174,7 @@
size="lg" size="lg"
square square
icon="info" icon="info"
color="info" :color="dash_info_color"
text-color="white" text-color="white"
/> />
<small>{{ summary.checks.info }} checks info</small> <small>{{ summary.checks.info }} checks info</small>
@@ -222,19 +256,34 @@ export default {
const store = useStore(); const store = useStore();
const selectedAgent = computed(() => store.state.selectedRow); const selectedAgent = computed(() => store.state.selectedRow);
const refreshSummaryTab = computed(() => store.state.refreshSummaryTab); 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 // summary tab logic
const summary = ref(null); const summary = ref(null);
const customFieldsDefinitions = ref(null); const customFieldsDefinitions = ref(null);
const loading = ref(false); 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) { function diskBarColor(percent) {
if (percent < 80) { if (percent < 80) {
return "positive"; return dash_positive_color.value;
} else if (percent > 80 && percent < 95) { } else if (percent > 80 && percent < 95) {
return "warning"; return dash_warning_color.value;
} else { } else {
return "negative"; return dash_negative_color.value;
} }
} }
@@ -290,6 +339,7 @@ export default {
async function refreshSummary() { async function refreshSummary() {
loading.value = true; loading.value = true;
summary.value = await fetchAgent(selectedAgent.value);
try { try {
const result = await refreshAgentWMI(selectedAgent.value); const result = await refreshAgentWMI(selectedAgent.value);
await getSummary(); await getSummary();
@@ -325,6 +375,12 @@ export default {
loading, loading,
selectedAgent, selectedAgent,
disks, disks,
dash_info_color,
dash_positive_color,
dash_warning_color,
dash_negative_color,
serial_number,
cpu,
// methods // methods
getSummary, getSummary,

View File

@@ -128,7 +128,7 @@
<q-icon <q-icon
v-else-if="props.row.action === 'ignore'" v-else-if="props.row.action === 'ignore'"
name="fas fa-check" name="fas fa-check"
color="negative" :color="dash_negative_color"
> >
<q-tooltip>Ignore</q-tooltip> <q-tooltip>Ignore</q-tooltip>
</q-icon> </q-icon>
@@ -144,7 +144,7 @@
<q-icon <q-icon
v-if="props.row.installed" v-if="props.row.installed"
name="fas fa-check" name="fas fa-check"
color="positive" :color="dash_positive_color"
> >
<q-tooltip>Installed</q-tooltip> <q-tooltip>Installed</q-tooltip>
</q-icon> </q-icon>
@@ -158,11 +158,15 @@
<q-icon <q-icon
v-else-if="props.row.action == 'ignore'" v-else-if="props.row.action == 'ignore'"
name="fas fa-ban" name="fas fa-ban"
color="negative" :color="dash_negative_color"
> >
<q-tooltip>Ignored</q-tooltip> <q-tooltip>Ignored</q-tooltip>
</q-icon> </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-tooltip>Missing</q-tooltip>
</q-icon> </q-icon>
</q-td> </q-td>
@@ -251,6 +255,9 @@ export default {
const tabHeight = computed(() => store.state.tabHeight); const tabHeight = computed(() => store.state.tabHeight);
const agentPlatform = computed(() => store.state.agentPlatform); const agentPlatform = computed(() => store.state.agentPlatform);
const formatDate = computed(() => store.getters.formatDate); 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 // setup quasar
const $q = useQuasar(); const $q = useQuasar();
@@ -348,6 +355,9 @@ export default {
selectedAgent, selectedAgent,
tabHeight, tabHeight,
agentPlatform, agentPlatform,
dash_positive_color,
dash_warning_color,
dash_negative_color,
// non-reactive data // non-reactive data
columns, columns,

View File

@@ -217,6 +217,7 @@
</template> </template>
<script> <script>
import { mapState } from "vuex";
import mixins from "@/mixins/mixins"; import mixins from "@/mixins/mixins";
import PolicyStatus from "@/components/automation/modals/PolicyStatus.vue"; import PolicyStatus from "@/components/automation/modals/PolicyStatus.vue";
import DiskSpaceCheck from "@/components/checks/DiskSpaceCheck.vue"; import DiskSpaceCheck from "@/components/checks/DiskSpaceCheck.vue";
@@ -268,6 +269,9 @@ export default {
if (newValue !== oldValue) this.getChecks(); if (newValue !== oldValue) this.getChecks();
}, },
}, },
computed: {
...mapState(["dash_positive_color", "dash_warning_color"]),
},
methods: { methods: {
getChecks() { getChecks() {
this.$q.loading.show(); this.$q.loading.show();
@@ -295,7 +299,9 @@ export default {
data.check_alert = true; data.check_alert = true;
const act = !action ? "enabled" : "disabled"; const act = !action ? "enabled" : "disabled";
const color = !action ? "positive" : "warning"; const color = !action
? this.dash_positive_color
: this.dash_warning_color;
this.$axios this.$axios
.put(`/checks/${id}/`, data) .put(`/checks/${id}/`, data)
.then(() => { .then(() => {

View File

@@ -41,7 +41,7 @@
<q-td v-if="props.row.status === 'passing'"> <q-td v-if="props.row.status === 'passing'">
<q-icon <q-icon
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="positive" :color="dash_positive_color"
name="check_circle" name="check_circle"
> >
<q-tooltip>Passing</q-tooltip> <q-tooltip>Passing</q-tooltip>
@@ -51,7 +51,7 @@
<q-icon <q-icon
v-if="props.row.alert_severity === 'info'" v-if="props.row.alert_severity === 'info'"
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="info" :color="dash_info_color"
name="info" name="info"
> >
<q-tooltip>Informational</q-tooltip> <q-tooltip>Informational</q-tooltip>
@@ -59,7 +59,7 @@
<q-icon <q-icon
v-else-if="props.row.alert_severity === 'warning'" v-else-if="props.row.alert_severity === 'warning'"
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="warning" :color="dash_warning_color"
name="warning" name="warning"
> >
<q-tooltip>Warning</q-tooltip> <q-tooltip>Warning</q-tooltip>
@@ -67,7 +67,7 @@
<q-icon <q-icon
v-else v-else
style="font-size: 1.3rem" style="font-size: 1.3rem"
color="negative" :color="dash_negative_color"
name="error" name="error"
> >
<q-tooltip>Error</q-tooltip> <q-tooltip>Error</q-tooltip>
@@ -148,7 +148,7 @@
<script> <script>
import { computed } from "vue"; import { computed } from "vue";
import { useStore } from "vuex"; import { useStore, mapState } from "vuex";
import ScriptOutput from "@/components/checks/ScriptOutput.vue"; import ScriptOutput from "@/components/checks/ScriptOutput.vue";
import EventLogCheckOutput from "@/components/checks/EventLogCheckOutput.vue"; import EventLogCheckOutput from "@/components/checks/EventLogCheckOutput.vue";
@@ -220,6 +220,12 @@ export default {
}; };
}, },
computed: { computed: {
...mapState([
"dash_info_color",
"dash_positive_color",
"dash_negative_color",
"dash_warning_color",
]),
title() { title() {
return !!this.item.readable_desc return !!this.item.readable_desc
? this.item.readable_desc + " Status" ? this.item.readable_desc + " Status"

View File

@@ -304,6 +304,9 @@ export default {
// setup vuex // setup vuex
const store = useStore(); const store = useStore();
const formatDate = computed(() => store.getters.formatDate); 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 // setup dropdowns
const { clientOptions, getClientOptions } = useClientDropdown(); const { clientOptions, getClientOptions } = useClientDropdown();
@@ -381,12 +384,18 @@ export default {
} }
function formatActionColor(action) { function formatActionColor(action) {
if (action === "add") return "success"; switch (action.toLowerCase()) {
else if (action === "agent_install") return "success"; case "modify":
else if (action === "modify") return "warning"; return dash_warning_color.value;
else if (action === "delete") return "negative"; case "add":
else if (action === "failed_login") return "negative"; case "agent_install":
else return "primary"; return dash_positive_color.value;
case "delete":
case "failed_login":
return dash_negative_color.value;
default:
return "primary";
}
} }
// watchers // watchers

View File

@@ -68,25 +68,25 @@
/> />
<q-radio <q-radio
v-model="logLevelFilter" v-model="logLevelFilter"
color="cyan" :color="dash_info_color"
val="info" val="info"
label="Info" label="Info"
/> />
<q-radio <q-radio
v-model="logLevelFilter" v-model="logLevelFilter"
color="red" :color="dash_negative_color"
val="critical" val="critical"
label="Critical" label="Critical"
/> />
<q-radio <q-radio
v-model="logLevelFilter" v-model="logLevelFilter"
color="red" :color="dash_negative_color"
val="error" val="error"
label="Error" label="Error"
/> />
<q-radio <q-radio
v-model="logLevelFilter" v-model="logLevelFilter"
color="yellow" :color="dash_warning_color"
val="warning" val="warning"
label="Warning" label="Warning"
/> />
@@ -109,7 +109,7 @@
<template v-slot:top-row> <template v-slot:top-row>
<q-tr v-if="Array.isArray(debugLog) && debugLog.length === 1000"> <q-tr v-if="Array.isArray(debugLog) && debugLog.length === 1000">
<q-td colspan="100%"> <q-td colspan="100%">
<q-icon name="warning" color="warning" /> <q-icon name="warning" :color="dash_warning_color" />
Results are limited to 1000 rows. Results are limited to 1000 rows.
</q-td> </q-td>
</q-tr> </q-tr>
@@ -203,6 +203,10 @@ export default {
const store = useStore(); const store = useStore();
const formatDate = computed(() => store.getters.formatDate); 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 // setup dropdowns
const { agentOptions, getAgentOptions } = useAgentDropdown(); const { agentOptions, getAgentOptions } = useAgentDropdown();
@@ -261,6 +265,10 @@ export default {
agentOptions, agentOptions,
loading, loading,
filter, filter,
dash_info_color,
dash_positive_color,
dash_warning_color,
dash_negative_color,
// non-reactive data // non-reactive data
columns, columns,

View File

@@ -94,7 +94,7 @@
class="q-pr-sm" class="q-pr-sm"
name="fas fa-signal" name="fas fa-signal"
size="1.2em" size="1.2em"
color="warning" :color="dash_warning_color"
/> />
Mark an agent as Mark an agent as
<span class="text-weight-bold">offline</span> if it has <span class="text-weight-bold">offline</span> if it has
@@ -120,7 +120,7 @@
class="q-pr-sm" class="q-pr-sm"
name="fas fa-signal" name="fas fa-signal"
size="1.2em" size="1.2em"
color="negative" :color="dash_negative_color"
/> />
Mark an agent as Mark an agent as
<span class="text-weight-bold">overdue</span> if it has <span class="text-weight-bold">overdue</span> if it has
@@ -373,6 +373,7 @@
</template> </template>
<script> <script>
import { mapState } from "vuex";
import { useDialogPluginComponent } from "quasar"; import { useDialogPluginComponent } from "quasar";
import mixins from "@/mixins/mixins"; import mixins from "@/mixins/mixins";
import PatchPolicyForm from "@/components/modals/agents/PatchPolicyForm.vue"; import PatchPolicyForm from "@/components/modals/agents/PatchPolicyForm.vue";
@@ -549,6 +550,9 @@ export default {
return result.trimEnd(","); return result.trimEnd(",");
}, },
}, },
computed: {
...mapState(["dash_warning_color", "dash_negative_color"]),
},
mounted() { mounted() {
// Get custom fields // Get custom fields
this.getCustomFields("agent").then((r) => { this.getCustomFields("agent").then((r) => {

View File

@@ -12,7 +12,7 @@
<q-tab name="urlactions" label="URL Actions" /> <q-tab name="urlactions" label="URL Actions" />
<q-tab name="retention" label="Retention" /> <q-tab name="retention" label="Retention" />
<q-tab name="apikeys" label="API Keys" /> <q-tab name="apikeys" label="API Keys" />
<q-tab name="openai" label="Open AI" /> <!-- <q-tab name="openai" label="Open AI" /> -->
</q-tabs> </q-tabs>
</template> </template>
<template v-slot:after> <template v-slot:after>
@@ -511,7 +511,7 @@
</q-tab-panel> </q-tab-panel>
<!-- Open AI --> <!-- Open AI -->
<q-tab-panel name="openai"> <!-- <q-tab-panel name="openai">
<div class="text-subtitle2">Open AI</div> <div class="text-subtitle2">Open AI</div>
<q-separator /> <q-separator />
<q-card-section class="row"> <q-card-section class="row">
@@ -551,7 +551,7 @@
</template> </template>
</q-input> </q-input>
</q-card-section> </q-card-section>
</q-tab-panel> </q-tab-panel> -->
</q-tab-panels> </q-tab-panels>
</q-scroll-area> </q-scroll-area>
<q-card-section class="row items-center"> <q-card-section class="row items-center">

View File

@@ -82,6 +82,98 @@
class="col-4" class="col-4"
/> />
</q-card-section> </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"> <q-card-section class="row">
<div class="col-2">Client Sort:</div> <div class="col-2">Client Sort:</div>
<div class="col-2"></div> <div class="col-2"></div>
@@ -156,9 +248,14 @@ export default {
tab: "ui", tab: "ui",
splitterModel: 20, splitterModel: 20,
loading_bar_color: "", loading_bar_color: "",
dash_info_color: "",
dash_positive_color: "",
dash_negative_color: "",
dash_warning_color: "",
urlActions: [], urlActions: [],
clear_search_when_switching: true, clear_search_when_switching: true,
date_format: "", date_format: "",
quasar_color_url: "https://quasar.dev/style/color-palette",
clientTreeSortOptions: [ clientTreeSortOptions: [
{ {
label: "Sort alphabetically, moving failing clients to the top", label: "Sort alphabetically, moving failing clients to the top",
@@ -235,6 +332,10 @@ export default {
this.defaultAgentTblTab = r.data.default_agent_tbl_tab; this.defaultAgentTblTab = r.data.default_agent_tbl_tab;
this.clientTreeSort = r.data.client_tree_sort; this.clientTreeSort = r.data.client_tree_sort;
this.loading_bar_color = r.data.loading_bar_color; 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.clear_search_when_switching = r.data.clear_search_when_switching;
this.date_format = r.data.date_format; this.date_format = r.data.date_format;
}); });
@@ -253,6 +354,10 @@ export default {
default_agent_tbl_tab: this.defaultAgentTblTab, default_agent_tbl_tab: this.defaultAgentTblTab,
client_tree_sort: this.clientTreeSort, client_tree_sort: this.clientTreeSort,
loading_bar_color: this.loading_bar_color, 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, clear_search_when_switching: this.clear_search_when_switching,
date_format: this.date_format, date_format: this.date_format,
}; };

View File

@@ -3,7 +3,7 @@
ref="dialogRef" ref="dialogRef"
@hide="onDialogHide" @hide="onDialogHide"
persistent persistent
@keydown.esc="onDialogHide" @keydown.esc.stop="onDialogHide"
:maximized="maximized" :maximized="maximized"
> >
<q-card <q-card

View File

@@ -286,15 +286,10 @@
</template> </template>
</q-tree> </q-tree>
</div> </div>
<q-table <tactical-table
v-if="tableView" v-if="tableView"
dense dense
:table-class="{
'table-bgcolor': !$q.dark.isActive,
'table-bgcolor-dark': $q.dark.isActive,
}"
:style="{ 'max-height': `${$q.screen.height - 182}px` }" :style="{ 'max-height': `${$q.screen.height - 182}px` }"
class="tbl-sticky"
:rows="visibleScripts" :rows="visibleScripts"
:columns="columns" :columns="columns"
:loading="loading" :loading="loading"
@@ -304,6 +299,7 @@
binary-state-sort binary-state-sort
virtual-scroll virtual-scroll
:rows-per-page-options="[0]" :rows-per-page-options="[0]"
column-select
> >
<template v-slot:header-cell-favorite="props"> <template v-slot:header-cell-favorite="props">
<q-th :props="props" auto-width> <q-th :props="props" auto-width>
@@ -425,7 +421,7 @@
</q-list> </q-list>
</q-menu> </q-menu>
<!-- favorite --> <!-- favorite -->
<q-td> <q-td key="favorite" :props="props">
<q-icon <q-icon
v-if="props.row.favorite" v-if="props.row.favorite"
color="yellow-8" color="yellow-8"
@@ -434,7 +430,7 @@
/> />
</q-td> </q-td>
<!-- shell icon --> <!-- shell icon -->
<q-td> <q-td key="shell" :props="props">
<q-icon <q-icon
v-if="props.row.shell === 'powershell'" v-if="props.row.shell === 'powershell'"
name="mdi-powershell" name="mdi-powershell"
@@ -469,7 +465,7 @@
</q-icon> </q-icon>
</q-td> </q-td>
<!-- supported platforms --> <!-- supported platforms -->
<q-td> <q-td key="supported_platforms" :props="props">
<q-badge <q-badge
v-if=" v-if="
!props.row.supported_platforms || !props.row.supported_platforms ||
@@ -487,7 +483,11 @@
> >
</q-td> </q-td>
<!-- name --> <!-- 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) }} {{ truncateText(props.row.name, 50) }}
<q-tooltip <q-tooltip
v-if="props.row.name.length >= 50" v-if="props.row.name.length >= 50"
@@ -497,7 +497,7 @@
</q-tooltip> </q-tooltip>
</q-td> </q-td>
<!-- args --> <!-- args -->
<q-td> <q-td key="args" :props="props">
<span v-if="props.row.args.length > 0"> <span v-if="props.row.args.length > 0">
{{ truncateText(props.row.args.toString(), 30) }} {{ truncateText(props.row.args.toString(), 30) }}
<q-tooltip <q-tooltip
@@ -509,8 +509,8 @@
</span> </span>
</q-td> </q-td>
<q-td>{{ props.row.category }}</q-td> <q-td key="category" :props="props">{{ props.row.category }}</q-td>
<q-td> <q-td key="desc" :props="props">
{{ truncateText(props.row.description, 30) }} {{ truncateText(props.row.description, 30) }}
<q-tooltip <q-tooltip
v-if="props.row.description.length >= 30" v-if="props.row.description.length >= 30"
@@ -518,10 +518,13 @@
>{{ props.row.description }}</q-tooltip >{{ props.row.description }}</q-tooltip
> >
</q-td> </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> </q-tr>
</template> </template>
</q-table> </tactical-table>
</q-card> </q-card>
</q-dialog> </q-dialog>
</template> </template>
@@ -545,12 +548,13 @@ import { notifySuccess } from "@/utils/notify";
import ScriptUploadModal from "@/components/scripts/ScriptUploadModal.vue"; import ScriptUploadModal from "@/components/scripts/ScriptUploadModal.vue";
import ScriptFormModal from "@/components/scripts/ScriptFormModal.vue"; import ScriptFormModal from "@/components/scripts/ScriptFormModal.vue";
import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue"; import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue";
import TacticalTable from "@/components/ui/TacticalTable.vue";
// static data // static data
const columns = [ const columns = [
{ {
name: "favorite", name: "favorite",
label: "", label: "Favorites",
field: "favorite", field: "favorite",
align: "left", align: "left",
sortable: true, sortable: true,
@@ -608,6 +612,9 @@ const columns = [
export default { export default {
name: "ScriptManager", name: "ScriptManager",
components: {
TacticalTable,
},
emits: [...useDialogPluginComponent.emits], emits: [...useDialogPluginComponent.emits],
setup() { setup() {
// setup vuex store // setup vuex store

View 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>

View File

@@ -1,4 +1,5 @@
import { ref } from "vue"; import { computed, ref } from "vue";
import { useStore } from "vuex";
import { fetchAgents } from "@/api/agents"; import { fetchAgents } from "@/api/agents";
import { formatAgentOptions } from "@/utils/format"; import { formatAgentOptions } from "@/utils/format";
@@ -28,10 +29,12 @@ export function useAgentDropdown() {
} }
export function cmdPlaceholder(shell) { export function cmdPlaceholder(shell) {
if (shell === "cmd") return "rmdir /S /Q C:\\Windows\\System32"; const store = useStore();
else if (shell === "powershell") const placeholders = computed(() => store.state.run_cmd_placeholder_text);
return "Remove-Item -Recurse -Force C:\\Windows\\System32";
else return "rm -rf --no-preserve-root /"; if (shell === "cmd") return placeholders.value.cmd;
else if (shell === "powershell") return placeholders.value.powershell;
else return placeholders.value.shell;
} }
export const agentPlatformOptions = [ export const agentPlatformOptions = [

View File

@@ -56,15 +56,27 @@
Tactical RMM<span class="text-overline q-ml-sm" Tactical RMM<span class="text-overline q-ml-sm"
>v{{ currentTRMMVersion }}</span >v{{ currentTRMMVersion }}</span
> >
<span class="text-overline q-ml-md" v-if="updateAvailable()" <!-- update check -->
><q-badge color="warning" <q-chip
><a :href="latestReleaseURL" target="_blank" v-if="updateAvailable"
>v{{ latestTRMMVersion }} available</a class="text-overline q-ml-sm"
></q-badge :color="dash_warning_color"
></span icon="update"
dense
><a :href="latestReleaseURL" target="_blank"
>v{{ latestTRMMVersion }} available</a
></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> </q-toolbar-title>
<!-- temp dark mode toggle --> <!-- temp dark mode toggle -->
<q-toggle <q-toggle
v-model="darkMode" v-model="darkMode"
@@ -94,7 +106,11 @@
</q-item> </q-item>
<q-item> <q-item>
<q-item-section avatar> <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>
<q-item-section no-wrap> <q-item-section no-wrap>
@@ -113,7 +129,11 @@
</q-item> </q-item>
<q-item> <q-item>
<q-item-section avatar> <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>
<q-item-section no-wrap> <q-item-section no-wrap>
@@ -218,6 +238,8 @@ export default {
const user = computed(() => store.state.username); const user = computed(() => store.state.username);
const hosted = computed(() => store.state.hosted); const hosted = computed(() => store.state.hosted);
const tokenExpired = computed(() => store.state.tokenExpired); 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(() => { const latestReleaseURL = computed(() => {
return latestTRMMVersion.value return latestTRMMVersion.value
@@ -255,6 +277,7 @@ export default {
const serverOfflineCount = ref(0); const serverOfflineCount = ref(0);
const workstationCount = ref(0); const workstationCount = ref(0);
const workstationOfflineCount = ref(0); const workstationOfflineCount = ref(0);
const daysUntilCertExpires = ref(100);
const ws = ref(null); const ws = ref(null);
@@ -262,6 +285,13 @@ export default {
// moved computed token inside the function since it is not refreshing // moved computed token inside the function since it is not refreshing
// when ws is closed causing ws to connect with expired token // when ws is closed causing ws to connect with expired token
const token = computed(() => store.state.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"); console.log("Starting websocket");
let url = getWSUrl("dashinfo", token.value); let url = getWSUrl("dashinfo", token.value);
ws.value = new WebSocket(url); ws.value = new WebSocket(url);
@@ -274,6 +304,7 @@ export default {
serverOfflineCount.value = data.total_server_offline_count; serverOfflineCount.value = data.total_server_offline_count;
workstationCount.value = data.total_workstation_count; workstationCount.value = data.total_workstation_count;
workstationOfflineCount.value = data.total_workstation_offline_count; workstationOfflineCount.value = data.total_workstation_offline_count;
daysUntilCertExpires.value = data.days_until_cert_expires;
}; };
ws.value.onclose = (e) => { ws.value.onclose = (e) => {
try { try {
@@ -294,16 +325,24 @@ export default {
const poll = ref(null); const poll = ref(null);
function livePoll() { function livePoll() {
poll.value = setInterval(() => { poll.value = setInterval(
store.dispatch("checkVer"); () => {
store.dispatch("getDashInfo", false); store.dispatch("checkVer");
}, 60 * 5 * 1000); store.dispatch("getDashInfo", false);
},
60 * 4 * 1000,
);
} }
function updateAvailable() { const updateAvailable = computed(() => {
if (latestTRMMVersion.value === "error" || hosted.value) return false; if (
latestTRMMVersion.value === "error" ||
hosted.value ||
currentTRMMVersion.value?.includes("-dev")
)
return false;
return currentTRMMVersion.value !== latestTRMMVersion.value; return currentTRMMVersion.value !== latestTRMMVersion.value;
} });
onMounted(() => { onMounted(() => {
setupWS(); setupWS();
@@ -324,6 +363,7 @@ export default {
serverOfflineCount, serverOfflineCount,
workstationCount, workstationCount,
workstationOfflineCount, workstationOfflineCount,
daysUntilCertExpires,
latestReleaseURL, latestReleaseURL,
currentTRMMVersion, currentTRMMVersion,
latestTRMMVersion, latestTRMMVersion,
@@ -332,6 +372,8 @@ export default {
darkMode, darkMode,
hosted, hosted,
tokenExpired, tokenExpired,
dash_warning_color,
dash_negative_color,
// methods // methods
showUserPreferences, showUserPreferences,

View File

@@ -34,6 +34,15 @@ export default function () {
latestTRMMVersion: null, latestTRMMVersion: null,
dateFormat: "MMM-DD-YYYY - HH:mm", dateFormat: "MMM-DD-YYYY - HH:mm",
openAIIntegrationEnabled: false, 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: { getters: {
@@ -140,6 +149,21 @@ export default function () {
setOpenAIIntegrationStatus(state, val) { setOpenAIIntegrationStatus(state, val) {
state.openAIIntegrationEnabled = 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: { actions: {
setClientTreeSplitter(context, val) { setClientTreeSplitter(context, val) {
@@ -164,9 +188,9 @@ export default function () {
} }
if (clearTreeSelected) commit("destroySubTable"); if (clearTreeSelected) commit("destroySubTable");
dispatch("getDashInfo", false);
dispatch("loadAgents"); dispatch("loadAgents");
dispatch("loadTree"); dispatch("loadTree");
dispatch("getDashInfo", false);
}, },
async loadAgents({ state, commit }) { async loadAgents({ state, commit }) {
commit("AGENT_TABLE_LOADING", true); commit("AGENT_TABLE_LOADING", true);
@@ -198,111 +222,111 @@ export default function () {
commit("AGENT_TABLE_LOADING", false); commit("AGENT_TABLE_LOADING", false);
}, },
async getDashInfo(context, edited = true) { async getDashInfo({ commit }, edited = true) {
const { data } = await axios.get("/core/dashinfo/"); 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) { if (edited) {
LoadingBar.setDefaults({ color: data.loading_bar_color }); LoadingBar.setDefaults({ color: data.loading_bar_color });
context.commit( commit(
"setClearSearchWhenSwitching", "setClearSearchWhenSwitching",
data.clear_search_when_switching data.clear_search_when_switching
); );
context.commit( commit("SET_DEFAULT_AGENT_TBL_TAB", data.default_agent_tbl_tab);
"SET_DEFAULT_AGENT_TBL_TAB", commit("SET_CLIENT_TREE_SORT", data.client_tree_sort);
data.default_agent_tbl_tab commit("SET_CLIENT_SPLITTER", data.client_tree_splitter);
);
context.commit("SET_CLIENT_TREE_SORT", data.client_tree_sort);
context.commit("SET_CLIENT_SPLITTER", data.client_tree_splitter);
} }
Dark.set(data.dark_mode); Dark.set(data.dark_mode);
context.commit("setCurrentTRMMVersion", data.trmm_version); commit("setCurrentTRMMVersion", data.trmm_version);
context.commit("setLatestTRMMVersion", data.latest_trmm_ver); commit("setLatestTRMMVersion", data.latest_trmm_ver);
context.commit("SET_AGENT_DBLCLICK_ACTION", data.dbl_click_action); commit("SET_AGENT_DBLCLICK_ACTION", data.dbl_click_action);
context.commit("SET_URL_ACTION", data.url_action); commit("SET_URL_ACTION", data.url_action);
context.commit("setShowCommunityScripts", data.show_community_scripts); commit("setShowCommunityScripts", data.show_community_scripts);
context.commit("SET_HOSTED", data.hosted); commit("SET_HOSTED", data.hosted);
context.commit("SET_TOKEN_EXPIRED", data.token_is_expired); commit("SET_TOKEN_EXPIRED", data.token_is_expired);
context.commit( commit("setOpenAIIntegrationStatus", data.open_ai_integration_enabled);
"setOpenAIIntegrationStatus", commit("setRunCmdPlaceholders", data.run_cmd_placeholder_text);
data.open_ai_integration_enabled
);
if (data.date_format && data.date_format !== "") if (data?.date_format !== "") commit("setDateFormat", data.date_format);
context.commit("setDateFormat", data.date_format); else commit("setDateFormat", data.default_date_format);
else context.commit("setDateFormat", data.default_date_format);
}, },
loadTree({ commit, state }) { loadTree({ commit, state }) {
axios setTimeout(() => {
.get("/clients/") axios
.then((r) => { .get("/clients/")
if (r.data.length === 0) { .then((r) => {
this.$router.push({ name: "InitialSetup" }); if (r.data.length === 0) {
} this.$router.push({ name: "InitialSetup" });
}
let output = []; let output = [];
for (let client of r.data) { for (let client of r.data) {
let childSites = []; let childSites = [];
for (let site of client.sites) { for (let site of client.sites) {
let siteNode = { let siteNode = {
label: site.name, label: site.name,
id: site.id, id: site.id,
raw: `Site|${site.id}`, raw: `Site|${site.id}`,
header: "generic", header: "generic",
icon: "apartment", icon: "apartment",
selectable: true, selectable: true,
site: site, site: site,
}; };
if (site.maintenance_mode) { if (site.maintenance_mode) {
siteNode["color"] = "green"; siteNode["color"] = "green";
} else if (site.failing_checks.error) { } else if (site.failing_checks.error) {
siteNode["color"] = "negative"; siteNode["color"] = "negative";
} else if (site.failing_checks.warning) { } else if (site.failing_checks.warning) {
siteNode["color"] = "warning"; siteNode["color"] = "warning";
}
childSites.push(siteNode);
} }
childSites.push(siteNode); let clientNode = {
label: client.name,
id: client.id,
raw: `Client|${client.id}`,
header: "root",
icon: "business",
children: childSites,
client: client,
};
if (client.maintenance_mode) clientNode["color"] = "green";
else if (client.failing_checks.error) {
clientNode["color"] = "negative";
} else if (client.failing_checks.warning) {
clientNode["color"] = "warning";
}
output.push(clientNode);
} }
let clientNode = { const sorted = output.sort((a, b) =>
label: client.name, a.label.localeCompare(b.label)
id: client.id, );
raw: `Client|${client.id}`, if (state.clientTreeSort === "alphafail") {
header: "root", // move failing clients to the top
icon: "business", const failing = sorted.filter(
children: childSites, (i) => i.color === "negative" || i.color === "warning"
client: client, );
}; const ok = sorted.filter(
(i) => i.color !== "negative" && i.color !== "warning"
if (client.maintenance_mode) clientNode["color"] = "green"; );
else if (client.failing_checks.error) { const sortedByFailing = [...failing, ...ok];
clientNode["color"] = "negative"; commit("loadTree", sortedByFailing);
} else if (client.failing_checks.warning) { } else {
clientNode["color"] = "warning"; commit("loadTree", sorted);
} }
})
output.push(clientNode); .catch(() => {
} state.treeReady = true;
});
const sorted = output.sort((a, b) => }, 150);
a.label.localeCompare(b.label)
);
if (state.clientTreeSort === "alphafail") {
// move failing clients to the top
const failing = sorted.filter(
(i) => i.color === "negative" || i.color === "warning"
);
const ok = sorted.filter(
(i) => i.color !== "negative" && i.color !== "warning"
);
const sortedByFailing = [...failing, ...ok];
commit("loadTree", sortedByFailing);
} else {
commit("loadTree", sorted);
}
})
.catch(() => {
state.treeReady = true;
});
}, },
checkVer(context) { checkVer(context) {
axios.get("/core/version/").then((r) => { axios.get("/core/version/").then((r) => {

View File

@@ -13,7 +13,7 @@
> >
<q-spinner size="40px" color="primary" /> <q-spinner size="40px" color="primary" />
</div> </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-list dense class="rounded-borders">
<q-item <q-item
clickable clickable
@@ -452,7 +452,7 @@ export default {
showInstallAgentModal: false, showInstallAgentModal: false,
sitePk: null, sitePk: null,
innerModel: (this.$q.screen.height - 82) / 2, 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, filterTextLength: 0,
filterAvailability: "all", filterAvailability: "all",
filterPatchesPending: false, filterPatchesPending: false,

View File

@@ -4,8 +4,17 @@
<div class="col"></div> <div class="col"></div>
<div class="col"> <div class="col">
<q-card> <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"> <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-card-section>
<q-form @submit.prevent="finish"> <q-form @submit.prevent="finish">
<q-card-section> <q-card-section>

View File

@@ -2,7 +2,7 @@
<div> <div>
<q-bar> <q-bar>
<span class="text-caption"> <span class="text-caption">
Agent Status: TRMM Agent Status:
<q-badge :color="statusColor" :label="status" /> <q-badge :color="statusColor" :label="status" />
</span> </span>
<q-space /> <q-space />
@@ -15,7 +15,7 @@
@click="restartMeshService" @click="restartMeshService"
/> />
<q-btn <q-btn
color="negative" :color="dash_negative_color"
size="sm" size="sm"
label="Recover Connection" label="Recover Connection"
icon="fas fa-first-aid" icon="fas fa-first-aid"
@@ -35,6 +35,7 @@
<script> <script>
// composition imports // composition imports
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted } from "vue";
import { useStore } from "vuex";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { useMeta, useQuasar } from "quasar"; import { useMeta, useQuasar } from "quasar";
import { fetchAgentMeshCentralURLs, sendAgentRecoverMesh } from "@/api/agents"; import { fetchAgentMeshCentralURLs, sendAgentRecoverMesh } from "@/api/agents";
@@ -47,12 +48,17 @@ export default {
setup() { setup() {
// vue lifecycle hooks // vue lifecycle hooks
onMounted(() => { onMounted(() => {
dashInfo();
getDashInfo(); getDashInfo();
getMeshURLs(); getMeshURLs();
}); });
// quasar setup // quasar setup
const $q = useQuasar(); 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 // vue router
const { params } = useRoute(); const { params } = useRoute();
@@ -64,14 +70,19 @@ export default {
const statusColor = computed(() => { const statusColor = computed(() => {
switch (status.value) { switch (status.value) {
case "online": case "online":
return "positive"; return dash_positive_color.value;
case "offline": case "offline":
return "warning"; return dash_warning_color.value;
default: 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() { async function getMeshURLs() {
$q.loading.show(); $q.loading.show();
try { try {
@@ -131,6 +142,7 @@ export default {
control, control,
status, status,
statusColor, statusColor,
dash_negative_color,
// methods // methods
repairMeshCentral, repairMeshCentral,