Compare commits

..

11 Commits

Author SHA1 Message Date
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
48 changed files with 4495 additions and 4166 deletions

View File

@@ -5,7 +5,7 @@
"esbenp.prettier-vscode",
"editorconfig.editorconfig",
"vue.volar",
"wayou.vscode-todo-highlight"
"wayou.vscode-todo-highlight",
],
"unwantedRecommendations": [
"octref.vetur",

View File

@@ -4,7 +4,7 @@
"editor.formatOnSave": true,
"[vue][javascript][typescript][javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": ["source.fixAll.eslint"]
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
},
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
"typescript.tsdk": "node_modules/typescript/lib",
@@ -15,7 +15,7 @@
"**/node_modules/": true,
"/node_modules/**": true,
"**/env/": true,
"/env/**": true
"/env/**": true,
}
}
}

View File

@@ -1,22 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title><%= productName %></title>
<meta charset="utf-8" />
<meta name="robots" content="noindex" />
<meta name="description" content="<%= productDescription %>" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta
name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"
/>
<link rel="icon" type="image/ico" href="favicon.ico" />
<script src="/env-config.js"></script>
</head>
<head>
<title>
<%= productName %>
</title>
<meta charset="utf-8" />
<meta name="robots" content="noindex" />
<meta name="description" content="<%= productDescription %>" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>" />
<link rel="icon" type="image/ico" href="favicon.ico" />
<script src="/env-config.js"></script>
</head>
<body>
<!-- quasar:entry-point -->
</body>
<body>
<!-- quasar:entry-point -->
</body>
</html>

7045
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "web",
"version": "0.101.21-dev",
"version": "0.101.7",
"private": true,
"productName": "Tactical RMM",
"scripts": {
@@ -10,13 +10,13 @@
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
},
"dependencies": {
"@quasar/extras": "1.16.4",
"apexcharts": "3.40.0",
"axios": "1.4.0",
"@quasar/extras": "1.15.5",
"apexcharts": "3.35.5",
"axios": "0.27.2",
"dotenv": "16.0.3",
"qrcode.vue": "3.4.0",
"quasar": "2.12.0",
"vue": "3.2.47",
"qrcode.vue": "3.3.3",
"quasar": "2.10.1",
"vue": "3.2.41",
"vue3-ace-editor": "2.2.2",
"vue3-apexcharts": "1.4.1",
"vuedraggable": "4.1.0",
@@ -24,17 +24,17 @@
"vuex": "4.1.0"
},
"devDependencies": {
"@quasar/cli": "^2.2.1",
"@intlify/unplugin-vue-i18n": "^0.10.0",
"@quasar/app-vite": "^1.4.3",
"@types/node": "^20.2.4",
"@typescript-eslint/eslint-plugin": "^5.59.7",
"@typescript-eslint/parser": "^5.59.7",
"autoprefixer": "10.4.14",
"eslint": "8.41.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-vue": "8.7.1",
"prettier": "2.8.8",
"typescript": "5.0.4"
"@quasar/cli": "^1.3.2",
"@intlify/vite-plugin-vue-i18n": "^6.0.3",
"@quasar/app-vite": "^1.1.3",
"@types/node": "^18.11.9",
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"autoprefixer": "^10.4.12",
"eslint": "^8.27.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-vue": "^8.5.0",
"prettier": "^2.7.1",
"typescript": "^4.8.4"
}
}

View File

@@ -4,18 +4,18 @@
module.exports = {
plugins: [
// https://github.com/postcss/autoprefixer
require("autoprefixer")({
require('autoprefixer')({
overrideBrowserslist: [
"last 4 Chrome versions",
"last 4 Firefox versions",
"last 4 Edge versions",
"last 4 Safari versions",
"last 4 Android versions",
"last 4 ChromeAndroid versions",
"last 4 FirefoxAndroid versions",
"last 4 iOS versions",
],
}),
'last 4 Chrome versions',
'last 4 Firefox versions',
'last 4 Edge versions',
'last 4 Safari versions',
'last 4 Android versions',
'last 4 ChromeAndroid versions',
'last 4 FirefoxAndroid versions',
'last 4 iOS versions'
]
})
// https://github.com/elchininet/postcss-rtlcss
// If you want to support RTL css, then
@@ -23,5 +23,5 @@ module.exports = {
// 2. optionally set quasar.config.js > framework > lang to an RTL language
// 3. uncomment the following line:
// require('postcss-rtlcss')
],
};
]
}

View File

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

View File

@@ -12,25 +12,6 @@ export async function fetchUsers(params = {}) {
}
}
export async function resetPass(pass) {
const payload = { password: pass };
try {
const { data } = await axios.put(`${baseUrl}/resetpw/`, payload);
return data;
} catch (e) {
console.error(e);
}
}
export async function resetTwoFactor() {
try {
const { data } = await axios.put(`${baseUrl}/reset2fa/`);
return data;
} catch (e) {
console.error(e);
}
}
// role api function
export async function fetchRoles(params = {}) {
try {

View File

@@ -232,8 +232,3 @@ 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;
}

View File

@@ -38,8 +38,3 @@ export async function runURLAction(payload) {
console.error(e);
}
}
export async function generateScript(payload) {
const { data } = await axios.post(`${baseUrl}/openai/generate/`, payload);
return data;
}

View File

@@ -211,7 +211,7 @@
v-if="props.row.maintenance_mode"
name="construction"
size="1.2em"
:color="dash_positive_color"
color="green"
>
<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="dash_negative_color"
color="negative"
>
<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="dash_warning_color"
color="warning"
>
<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="dash_info_color"
color="info"
>
<q-tooltip>Checks info</q-tooltip>
</q-icon>
@@ -243,7 +243,7 @@
v-else
name="fas fa-check-double"
size="1.2em"
:color="dash_positive_color"
color="positive"
>
<q-tooltip>Checks passing</q-tooltip>
</q-icon>
@@ -279,7 +279,7 @@
@click="showPendingActionsModal(props.row)"
name="far fa-clock"
size="1.4em"
:color="dash_warning_color"
color="warning"
class="cursor-pointer"
>
<q-tooltip
@@ -303,7 +303,7 @@
v-if="props.row.status === 'overdue'"
name="fas fa-signal"
size="1.2em"
:color="dash_negative_color"
color="negative"
>
<q-tooltip>Agent overdue</q-tooltip>
</q-icon>
@@ -311,16 +311,11 @@
v-else-if="props.row.status === 'offline'"
name="fas fa-signal"
size="1.2em"
:color="dash_warning_color"
color="warning"
>
<q-tooltip>Agent offline</q-tooltip>
</q-icon>
<q-icon
v-else
name="fas fa-signal"
size="1.2em"
:color="dash_positive_color"
>
<q-icon v-else name="fas fa-signal" size="1.2em" color="positive">
<q-tooltip>Agent online</q-tooltip>
</q-icon>
</q-td>
@@ -378,13 +373,17 @@ export default {
"local_ips",
"make_model",
"physical_disks",
"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
// https://github.com/amidaware/tacticalrmm/issues/1264
const allColumns = [...cols, ...hiddenFields.map((field) => ({ field }))];
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;
@@ -438,12 +437,8 @@ export default {
}
// Normal text filter
return allColumns.some((col) => {
let valObj = cellValue(col, row);
if (Array.isArray(valObj)) {
valObj = valObj.map((item) => (item.value ? item.value : item));
}
const val = valObj + "";
return cols.some((col) => {
const val = cellValue(col, row) + "";
const haystack =
val === "undefined" || val === "null" ? "" : val.toLowerCase();
return haystack.indexOf(search) !== -1;
@@ -494,9 +489,7 @@ export default {
const data = {
[db_field]: !alert_action,
};
const alertColor = !alert_action
? this.dash_positive_color
: this.dash_info_color;
const alertColor = !alert_action ? "positive" : "info";
this.$axios.put(`/agents/${agent.agent_id}/`, data).then(() => {
this.$q.notify({
color: alertColor,
@@ -540,13 +533,7 @@ export default {
},
},
computed: {
...mapState([
"tableHeight",
"dash_info_color",
"dash_positive_color",
"dash_negative_color",
"dash_warning_color",
]),
...mapState(["tableHeight"]),
agentDblClickAction() {
return this.$store.state.agentDblClickAction;
},

View File

@@ -3,7 +3,7 @@
<q-badge v-if="alertsCount > 0" :color="badgeColor" floating transparent>{{
alertsCountText()
}}</q-badge>
<q-menu :style="{ 'max-height': `${$q.screen.height - 100}px` }">
<q-menu style="max-height: 30vh">
<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,7 +59,6 @@
</template>
<script>
import { mapState } from "vuex";
import mixins from "@/mixins/mixins";
import AlertsOverview from "@/components/modals/alerts/AlertsOverview.vue";
import { getTimeLapse } from "@/utils/format";
@@ -76,21 +75,19 @@ 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.dash_negative_color;
else if (severities.includes("warning")) return this.dash_warning_color;
else return this.dash_info_color;
if (severities.includes("error")) return this.errorColor;
else if (severities.includes("warning")) return this.warningColor;
else return this.infoColor;
},
},
methods: {
@@ -162,9 +159,9 @@ export default {
});
},
alertIconColor(severity) {
if (severity === "error") return this.dash_negative_color;
else if (severity === "warning") return this.dash_warning_color;
else return this.dash_info_color;
if (severity === "error") return this.errorColor;
else if (severity === "warning") return this.warningColor;
else return this.infoColor;
},
alertsCountText() {
if (this.alertsCount > 99) return "99+";

View File

@@ -1,75 +0,0 @@
<template>
<q-dialog ref="dialogRef" @hide="onDialogHide">
<q-card class="q-dialog-plugin" style="width: 60vw">
<q-card-section class="row">
<div class="col-3">New password:</div>
<div class="col-9">
<q-input
outlined
dense
v-model="pass"
:type="isPwd ? 'password' : 'text'"
:rules="[(val) => !!val || '*Required']"
>
<template v-slot:append>
<q-icon
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
<div class="col-3">Confirm password:</div>
<div class="col-9">
<q-input
outlined
dense
v-model="pass2"
:type="isPwd ? 'password' : 'text'"
:rules="[(val) => val === pass || 'Passwords do not match']"
>
<template v-slot:append>
<q-icon
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn
color="primary"
label="Reset"
@click="onOKClick"
:disable="!pass || pass !== pass2"
/>
<q-btn color="negative" label="Cancel" @click="onDialogCancel" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup>
import { ref } from "vue";
import { useDialogPluginComponent } from "quasar";
import { resetPass } from "@/api/accounts";
import { notifySuccess } from "@/utils/notify";
const pass = ref("");
const pass2 = ref("");
const isPwd = ref(true);
defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
useDialogPluginComponent();
async function onOKClick() {
const ret = await resetPass(pass.value);
notifySuccess(ret);
onDialogOK();
}
</script>

View File

@@ -98,10 +98,6 @@
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"
@@ -441,8 +437,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,

View File

@@ -146,13 +146,6 @@
<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" />
@@ -217,7 +210,6 @@ import {
removeAgent,
runRemoteBackground,
runTakeControl,
wakeUpWOL,
} from "@/api/agents";
import { runAgentUpdateScan, runAgentUpdateInstall } from "@/api/winupdates";
import { runAgentChecks } from "@/api/checks";
@@ -378,15 +370,6 @@ 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,
@@ -515,7 +498,6 @@ export default {
showPolicyAdd,
showAgentRecovery,
pingAgent,
wakeUp,
};
},
};

View File

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

View File

@@ -301,7 +301,7 @@
<q-td v-else-if="props.row.check_result.status === 'passing'">
<q-icon
style="font-size: 1.3rem"
:color="dash_positive_color"
color="positive"
name="check_circle"
>
<q-tooltip>Passing</q-tooltip>
@@ -311,7 +311,7 @@
<q-icon
v-if="getAlertSeverity(props.row) === 'info'"
style="font-size: 1.3rem"
:color="dash_info_color"
color="info"
name="info"
>
<q-tooltip>Informational</q-tooltip>
@@ -319,7 +319,7 @@
<q-icon
v-else-if="getAlertSeverity(props.row) === 'warning'"
style="font-size: 1.3rem"
:color="dash_warning_color"
color="warning"
name="warning"
>
<q-tooltip>Warning</q-tooltip>
@@ -327,7 +327,7 @@
<q-icon
v-else
style="font-size: 1.3rem"
:color="dash_negative_color"
color="negative"
name="error"
>
<q-tooltip>Error</q-tooltip>
@@ -479,10 +479,6 @@ 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();
@@ -657,10 +653,6 @@ export default {
tabHeight,
selectedAgent,
agentPlatform,
dash_info_color,
dash_positive_color,
dash_warning_color,
dash_negative_color,
// non-reactive data
columns,

View File

@@ -166,7 +166,7 @@ export default {
type: "textarea",
isValid: (val) => !!val,
},
style: "width: 90vw; max-width: 90vw",
style: "width: 30vw; max-width: 50vw;",
ok: { label: "Add" },
cancel: true,
}).onOk(async () => {
@@ -193,7 +193,7 @@ export default {
type: "textarea",
isValid: (val) => !!val,
},
style: "width: 90vw; max-width: 90vw",
style: "width: 30vw; max-width: 50vw;",
ok: { label: "Save" },
cancel: true,
}).onOk(async (data) => {

View File

@@ -18,33 +18,6 @@
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">
&bull; <q-badge color="green"> Maintenance Mode </q-badge>
@@ -87,7 +60,7 @@
</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-section avatar>
<q-icon name="fas fa-microchip" />
</q-item-section>
@@ -114,13 +87,6 @@
</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" />
@@ -144,7 +110,7 @@
size="lg"
square
icon="done"
:color="dash_positive_color"
color="green"
text-color="white"
/>
<small>{{ summary.checks.passing }} checks passing</small>
@@ -154,7 +120,7 @@
size="lg"
square
icon="cancel"
:color="dash_negative_color"
color="red"
text-color="white"
/>
<small>{{ summary.checks.failing }} checks failing</small>
@@ -164,7 +130,7 @@
size="lg"
square
icon="warning"
:color="dash_warning_color"
color="warning"
text-color="white"
/>
<small>{{ summary.checks.warning }} checks warning</small>
@@ -174,7 +140,7 @@
size="lg"
square
icon="info"
:color="dash_info_color"
color="info"
text-color="white"
/>
<small>{{ summary.checks.info }} checks info</small>
@@ -192,20 +158,6 @@
>
</div>
<div v-else>No checks</div>
<span
v-if="customFields.length > 0"
class="text-subtitle2 text-bold block q-mt-xl"
>Custom Fields</span
>
<q-list dense>
<q-item v-for="(field, i) in customFields" :key="field + i">
<q-item-section thumbnail>
<q-icon name="fas fa-user" size="xs" />
</q-item-section>
<q-item-section>{{ field.name }}: {{ field.value }}</q-item-section>
</q-item>
</q-list>
</div>
<div class="col-1"></div>
<!-- right -->
@@ -241,7 +193,6 @@ import {
openAgentWindow,
} from "@/api/agents";
import { notifySuccess } from "@/utils/notify";
import { fetchCustomFields } from "@/api/core";
// ui imports
import AgentActionMenu from "@/components/agents/AgentActionMenu.vue";
@@ -256,34 +207,18 @@ 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 dash_positive_color.value;
return "positive";
} else if (percent > 80 && percent < 95) {
return dash_warning_color.value;
return "warning";
} else {
return dash_negative_color.value;
return "negative";
}
}
@@ -301,37 +236,9 @@ export default {
return ret;
});
const customFields = computed(() => {
if (!summary.value.custom_fields) {
return [];
}
if (!customFieldsDefinitions.value) {
return [];
}
const ret = [];
for (const customField of summary.value.custom_fields) {
const definition = customFieldsDefinitions.value.find(
(def) => def.id === customField.field
);
if (
definition &&
!definition.hide_in_ui &&
customField.value?.length > 0
) {
ret.push({
name: definition.name,
value: customField.value,
});
}
}
return ret;
});
async function getSummary() {
loading.value = true;
summary.value = await fetchAgent(selectedAgent.value);
customFieldsDefinitions.value = await fetchCustomFields();
store.commit("setRefreshSummaryTab", false);
store.commit("setAgentPlatform", summary.value.plat);
loading.value = false;
@@ -339,7 +246,6 @@ export default {
async function refreshSummary() {
loading.value = true;
summary.value = await fetchAgent(selectedAgent.value);
try {
const result = await refreshAgentWMI(selectedAgent.value);
await getSummary();
@@ -371,16 +277,9 @@ export default {
return {
// reactive data
summary,
customFields,
loading,
selectedAgent,
disks,
dash_info_color,
dash_positive_color,
dash_warning_color,
dash_negative_color,
serial_number,
cpu,
// methods
getSummary,

View File

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

View File

@@ -8,16 +8,16 @@
v
}}</q-badge>
<q-btn
v-if="!!v"
size="sm"
class="q-ml-xs"
flat
round
icon="content_copy"
@click="copyValueToClip(v)"
>
<q-tooltip>Copy to Clipboard</q-tooltip>
</q-btn>
v-if="!!v"
size="sm"
class="q-ml-xs"
flat
round
icon="content_copy"
@click="copyValueToClip(v)"
>
<q-tooltip>Copy to Clipboard</q-tooltip>
</q-btn>
</div>
</div>
<q-separator v-if="info.length > 1" />
@@ -42,9 +42,10 @@ export default {
const tabHeight = computed(() => store.state.tabHeight);
function copyValueToClip(val) {
copyToClipboard(val).then(() => {
notifySuccess("Copied to clipboard");
});
copyToClipboard(val)
.then(() => {
notifySuccess("Copied to clipboard");
})
}
return {

View File

@@ -217,7 +217,6 @@
</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";
@@ -269,9 +268,6 @@ export default {
if (newValue !== oldValue) this.getChecks();
},
},
computed: {
...mapState(["dash_positive_color", "dash_warning_color"]),
},
methods: {
getChecks() {
this.$q.loading.show();
@@ -299,9 +295,7 @@ export default {
data.check_alert = true;
const act = !action ? "enabled" : "disabled";
const color = !action
? this.dash_positive_color
: this.dash_warning_color;
const color = !action ? "positive" : "warning";
this.$axios
.put(`/checks/${id}/`, data)
.then(() => {

View File

@@ -41,7 +41,7 @@
<q-td v-if="props.row.status === 'passing'">
<q-icon
style="font-size: 1.3rem"
:color="dash_positive_color"
color="positive"
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="dash_info_color"
color="info"
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="dash_warning_color"
color="warning"
name="warning"
>
<q-tooltip>Warning</q-tooltip>
@@ -67,7 +67,7 @@
<q-icon
v-else
style="font-size: 1.3rem"
:color="dash_negative_color"
color="negative"
name="error"
>
<q-tooltip>Error</q-tooltip>
@@ -148,7 +148,7 @@
<script>
import { computed } from "vue";
import { useStore, mapState } from "vuex";
import { useStore } from "vuex";
import ScriptOutput from "@/components/checks/ScriptOutput.vue";
import EventLogCheckOutput from "@/components/checks/EventLogCheckOutput.vue";
@@ -220,12 +220,6 @@ 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"

View File

@@ -39,19 +39,6 @@
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<q-select
dense
:label="envVarsLabel"
filled
v-model="state.env_vars"
use-input
use-chips
multiple
hide-dropdown-icon
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<tactical-dropdown
label="Informational return codes (press Enter after typing each code)"
@@ -128,7 +115,6 @@ import { useDialogPluginComponent } from "quasar";
import { useCheckModal } from "@/composables/checks";
import { useScriptDropdown } from "@/composables/scripts";
import { validateRetcode } from "@/utils/validation";
import { envVarsLabel } from "@/constants/constants";
// ui imports
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
@@ -146,15 +132,10 @@ export default {
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
// setup script dropdown
const {
script,
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
} = useScriptDropdown(props.check ? props.check.script : undefined, {
onMount: true,
});
const { script, scriptOptions, defaultTimeout, defaultArgs } =
useScriptDropdown(props.check ? props.check.script : undefined, {
onMount: true,
});
// check logic
const { state, loading, submit, failOptions, severityOptions } =
@@ -164,7 +145,6 @@ export default {
...props.parent,
script,
script_args: defaultArgs,
env_vars: defaultEnvVars,
timeout: defaultTimeout,
check_type: "script",
fails_b4_alert: 1,
@@ -183,7 +163,6 @@ export default {
failOptions,
scriptOptions,
severityOptions,
envVarsLabel,
// methods
submit,

View File

@@ -122,7 +122,7 @@ export default {
try {
const result = props.APIKey
? await editAPIKey(data.id, data)
? await editAPIKey(data)
: await saveAPIKey(data);
onDialogOK();
notifySuccess(result);

View File

@@ -304,9 +304,6 @@ 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();
@@ -384,18 +381,12 @@ export default {
}
function formatActionColor(action) {
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";
}
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";
}
// watchers

View File

@@ -68,25 +68,25 @@
/>
<q-radio
v-model="logLevelFilter"
:color="dash_info_color"
color="cyan"
val="info"
label="Info"
/>
<q-radio
v-model="logLevelFilter"
:color="dash_negative_color"
color="red"
val="critical"
label="Critical"
/>
<q-radio
v-model="logLevelFilter"
:color="dash_negative_color"
color="red"
val="error"
label="Error"
/>
<q-radio
v-model="logLevelFilter"
:color="dash_warning_color"
color="yellow"
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="dash_warning_color" />
<q-icon name="warning" color="warning" />
Results are limited to 1000 rows.
</q-td>
</q-tr>
@@ -203,10 +203,6 @@ 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();
@@ -265,10 +261,6 @@ export default {
agentOptions,
loading,
filter,
dash_info_color,
dash_positive_color,
dash_warning_color,
dash_negative_color,
// non-reactive data
columns,

View File

@@ -89,8 +89,7 @@
<p class="text-italic">
Note: the auth token above will be valid for {{ info.expires }} hours.
</p>
<q-btn
v-if="info.plat === 'windows'"
<q-btn v-if="info.plat === 'windows'"
type="a"
:href="info.data.url"
color="primary"

View File

@@ -102,18 +102,6 @@
new-value-mode="add"
/>
</q-card-section>
<q-card-section v-if="mode === 'script'" class="q-pt-none">
<tactical-dropdown
v-model="state.env_vars"
:label="envVarsLabel"
filled
use-input
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
</q-card-section>
<q-card-section v-if="mode === 'command'">
<p>Shell</p>
@@ -220,7 +208,7 @@ import { runBulkAction } from "@/api/agents";
import { notifySuccess } from "@/utils/notify";
import { cmdPlaceholder } from "@/composables/agents";
import { removeExtraOptionCategories } from "@/utils/format";
import { envVarsLabel, runAsUserToolTip } from "@/constants/constants";
import { runAsUserToolTip } from "@/constants/constants";
// ui imports
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
@@ -296,7 +284,6 @@ export default {
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
getScriptOptions,
} = useScriptDropdown();
const { agents, agentOptions, getAgentOptions } = useAgentDropdown();
@@ -320,7 +307,6 @@ export default {
script,
timeout: defaultTimeout,
args: defaultArgs,
env_vars: defaultEnvVars,
run_as_user: false,
});
const loading = ref(false);
@@ -418,7 +404,6 @@ export default {
targetOptions,
patchModeOptions,
runAsUserToolTip,
envVarsLabel,
//computed
modalTitle,

View File

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

View File

@@ -77,18 +77,6 @@
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<tactical-dropdown
v-model="state.env_vars"
:label="envVarsLabel"
filled
use-input
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<q-option-group
v-model="state.output"
@@ -190,7 +178,7 @@ import { useScriptDropdown } from "@/composables/scripts";
import { useCustomFieldDropdown } from "@/composables/core";
import { runScript } from "@/api/agents";
import { notifySuccess } from "@/utils/notify";
import { envVarsLabel, runAsUserToolTip } from "@/constants/constants";
import { runAsUserToolTip } from "@/constants/constants";
import {
formatScriptSyntax,
removeExtraOptionCategories,
@@ -221,18 +209,11 @@ export default {
const { dialogRef, onDialogHide } = useDialogPluginComponent();
// setup dropdowns
const {
script,
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
syntax,
link,
} = useScriptDropdown(props.script, {
onMount: true,
filterByPlatform: props.agent.plat,
});
const { script, scriptOptions, defaultTimeout, defaultArgs, syntax, link } =
useScriptDropdown(props.script, {
onMount: true,
filterByPlatform: props.agent.plat,
});
const { customFieldOptions } = useCustomFieldDropdown({ onMount: true });
// main run script functionaity
@@ -244,7 +225,6 @@ export default {
save_all_output: false,
script,
args: defaultArgs,
env_vars: defaultEnvVars,
timeout: defaultTimeout,
run_as_user: false,
});
@@ -301,7 +281,6 @@ export default {
// non-reactive data
outputOptions,
runAsUserToolTip,
envVarsLabel,
//methods
formatScriptSyntax,

View File

@@ -204,20 +204,6 @@
new-value-mode="add"
/>
<q-select
class="q-mb-sm"
dense
label="Failure action environment vars (press Enter after typing each key=value pair)"
filled
v-model="template.action_env_vars"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-input
class="q-mb-sm"
label="Failure action timeout (seconds)"
@@ -291,20 +277,6 @@
new-value-mode="add"
/>
<q-select
class="q-mb-sm"
dense
label="Resolved action environment vars (press Enter after typing each key=value pair)"
filled
v-model="template.resolved_action_env_vars"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-input
class="q-mb-sm"
label="Resolved action timeout (seconds)"
@@ -724,11 +696,9 @@ export default {
is_active: true,
action: null,
action_args: [],
action_env_vars: [],
action_timeout: 15,
resolved_action: null,
resolved_action_args: [],
resolved_action_env_vars: [],
resolved_action_timeout: 15,
email_recipients: [],
email_from: "",
@@ -792,13 +762,11 @@ export default {
(i) => i.value === this.template.action
);
this.template.action_args = script.args;
this.template.action_env_vars = script.env_vars;
} else if (type === "resolved") {
const script = this.scriptOptions.find(
(i) => i.value === this.template.resolved_action
);
this.template.resolved_action_args = script.args;
this.template.resolved_action_env_vars = script.env_vars;
}
},
toggleAddEmail() {

View File

@@ -12,7 +12,6 @@
<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-tabs>
</template>
<template v-slot:after>
@@ -509,49 +508,6 @@
<q-tab-panel name="apikeys">
<APIKeysTable />
</q-tab-panel>
<!-- Open AI -->
<!-- <q-tab-panel name="openai">
<div class="text-subtitle2">Open AI</div>
<q-separator />
<q-card-section class="row">
<div class="col-4">API Key:</div>
<div class="col-2"></div>
<q-input
dense
outlined
v-model="settings.open_ai_token"
class="col-6"
/>
</q-card-section>
<q-card-section class="row">
<div class="col-4">Open AI Model:</div>
<div class="col-2"></div>
<q-input
dense
outlined
v-model="settings.open_ai_model"
class="col-6"
>
<template v-slot:after>
<q-btn
round
dense
flat
icon="info"
size="sm"
@click="
openURL(
'https://platform.openai.com/docs/models/overview'
)
"
>
<q-tooltip>Click to see available options</q-tooltip>
</q-btn>
</template>
</q-input>
</q-card-section>
</q-tab-panel> -->
</q-tab-panels>
</q-scroll-area>
<q-card-section class="row items-center">

View File

@@ -82,98 +82,6 @@
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>
@@ -248,14 +156,9 @@ 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",
@@ -332,10 +235,6 @@ 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;
});
@@ -354,10 +253,6 @@ 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,
};

View File

@@ -11,17 +11,7 @@
:style="maximized ? '' : 'width: 90vw; max-width: 90vw'"
>
<q-bar>
<span class="q-pr-sm">{{ title }}</span>
<q-btn
v-if="!script && openAIEnabled"
size="xs"
:disable="loading"
dense
label="Generate Script"
color="primary"
no-caps
@click="generateScriptOpenAI"
/>
{{ title }}
<q-space />
<q-btn
dense
@@ -67,133 +57,105 @@
><br />Add one to get rid of this warning. Ignore if windows.
</q-banner>
<div class="row q-pa-sm">
<q-scroll-area
:thumb-style="{
right: '4px',
borderRadius: '5px',
width: '5px',
opacity: 0.75,
}"
:bar-style="{
right: '2px',
borderRadius: '9px',
width: '9px',
opacity: 0.2,
}"
class="col-4 q-mb-none q-pb-none"
:style="{ height: `${maximized ? '82vh' : '64vh'}` }"
>
<div class="q-gutter-sm q-pr-sm">
<q-input
filled
dense
:readonly="readonly"
v-model="formScript.name"
label="Name"
:rules="[(val) => !!val || '*Required']"
hide-bottom-space
/>
<q-input
filled
dense
:readonly="readonly"
v-model="formScript.description"
label="Description"
/>
<q-select
:readonly="readonly"
options-dense
filled
dense
v-model="formScript.shell"
:options="shellOptions"
emit-value
map-options
label="Shell Type"
/>
<tactical-dropdown
v-model="formScript.supported_platforms"
:options="agentPlatformOptions"
label="Supported Platforms (All supported if blank)"
clearable
mapOptions
filled
multiple
:readonly="readonly"
/>
<tactical-dropdown
filled
v-model="formScript.category"
:options="categories"
use-input
clearable
new-value-mode="add-unique"
filterable
label="Category"
:readonly="readonly"
hide-bottom-space
/>
<tactical-dropdown
v-model="formScript.args"
label="Script Arguments (press Enter after typing each argument)"
filled
use-input
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
:readonly="readonly"
/>
<tactical-dropdown
v-model="formScript.env_vars"
:label="envVarsLabel"
filled
use-input
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
:readonly="readonly"
/>
<q-input
type="number"
filled
dense
:readonly="readonly"
v-model.number="formScript.default_timeout"
label="Timeout (seconds)"
:rules="[(val) => val >= 5 || 'Minimum is 5']"
hide-bottom-space
/>
<q-checkbox
v-model="formScript.run_as_user"
label="Run As User (Windows only)"
>
<q-tooltip
>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.
</q-tooltip>
</q-checkbox>
<q-input
label="Syntax"
type="textarea"
style="height: 150px; overflow-y: auto; resize: none"
v-model="formScript.syntax"
dense
filled
:readonly="readonly"
/>
</div>
</q-scroll-area>
<div class="col-4 q-gutter-sm q-pr-sm">
<q-input
filled
dense
:readonly="readonly"
v-model="formScript.name"
label="Name"
:rules="[(val) => !!val || '*Required']"
hide-bottom-space
/>
<q-input
filled
dense
:readonly="readonly"
v-model="formScript.description"
label="Description"
/>
<q-select
:readonly="readonly"
options-dense
filled
dense
v-model="formScript.shell"
:options="shellOptions"
emit-value
map-options
label="Shell Type"
/>
<tactical-dropdown
v-model="formScript.supported_platforms"
:options="agentPlatformOptions"
label="Supported Platforms (All supported if blank)"
clearable
mapOptions
filled
multiple
:readonly="readonly"
/>
<tactical-dropdown
filled
v-model="formScript.category"
:options="categories"
use-input
clearable
new-value-mode="add-unique"
filterable
label="Category"
:readonly="readonly"
hide-bottom-space
/>
<tactical-dropdown
v-model="formScript.args"
label="Script Arguments (press Enter after typing each argument)"
filled
use-input
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
:readonly="readonly"
/>
<q-input
type="number"
filled
dense
:readonly="readonly"
v-model.number="formScript.default_timeout"
label="Timeout (seconds)"
:rules="[(val) => val >= 5 || 'Minimum is 5']"
hide-bottom-space
/>
<q-checkbox
v-model="formScript.run_as_user"
label="Run As User (Windows only)"
>
<q-tooltip
>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. Not supported on Windows Server.
</q-tooltip>
</q-checkbox>
<q-input
label="Syntax"
type="textarea"
style="height: 150px; overflow-y: auto; resize: none"
v-model="formScript.syntax"
dense
filled
:readonly="readonly"
/>
</div>
<v-ace-editor
v-model:value="formScript.script_body"
class="col-8"
:lang="lang"
:theme="$q.dark.isActive ? 'tomorrow_night_eighties' : 'tomorrow'"
:style="{ height: `${maximized ? '82vh' : '64vh'}` }"
:style="{ height: `${maximized ? '87vh' : '64vh'}` }"
wrap
:printMargin="false"
:options="{ fontSize: '14px' }"
@@ -247,11 +209,9 @@
<script>
// composable imports
import { ref, computed, onMounted } from "vue";
import { useStore } from "vuex";
import { useQuasar, useDialogPluginComponent } from "quasar";
import { saveScript, editScript, downloadScript } from "@/api/scripts";
import { useAgentDropdown, agentPlatformOptions } from "@/composables/agents";
import { generateScript } from "@/api/core";
import { notifySuccess } from "@/utils/notify";
// ui imports
@@ -269,7 +229,6 @@ import "ace-builds/src-noconflict/theme-tomorrow";
// static data
import { shellOptions } from "@/composables/scripts";
import { envVarsLabel } from "@/constants/constants";
export default {
name: "ScriptFormModal",
@@ -295,10 +254,6 @@ export default {
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
const $q = useQuasar();
// setup store
const store = useStore();
const openAIEnabled = computed(() => store.state.openAIIntegrationEnabled);
// setup agent dropdown
const { agent, agentOptions, getAgentOptions } = useAgentDropdown();
@@ -311,7 +266,6 @@ export default {
args: [],
script_body: "",
run_as_user: false,
env_vars: [],
});
if (props.clone) script.value.name = `(Copy) ${script.value.name}`;
@@ -388,23 +342,6 @@ export default {
});
}
function generateScriptOpenAI() {
$q.dialog({
title: "Ask ChatGPT what you need!",
prompt: {
model: `${lang.value} code that `,
type: "text",
},
cancel: true,
persistent: true,
}).onOk(async (data) => {
const completion = await generateScript({
prompt: data,
});
script.value.script_body = completion;
});
}
// component life cycle hooks
onMounted(async () => {
agentLoading.value = true;
@@ -426,16 +363,13 @@ export default {
// non-reactive data
shellOptions,
agentPlatformOptions,
envVarsLabel,
//computed
title,
openAIEnabled,
//methods
submitForm,
openTestScriptModal,
generateScriptOpenAI,
// quasar dialog plugin
dialogRef,

View File

@@ -11,17 +11,7 @@
:style="maximized ? '' : 'width: 70vw; max-width: 90vw'"
>
<q-bar>
<span class="q-pr-sm">{{ title }}</span>
<q-btn
v-if="!snippet && openAIEnabled"
:disable="loading"
dense
size="xs"
label="Generate Script"
color="primary"
no-caps
@click="generateScriptOpenAI"
/>
{{ title }}
<q-space />
<q-btn
dense
@@ -107,9 +97,6 @@
<script>
// composable imports
import { ref, computed } from "vue";
import { useStore } from "vuex";
import { useQuasar } from "quasar";
import { generateScript } from "@/api/core";
import { useDialogPluginComponent } from "quasar";
import { saveScriptSnippet, editScriptSnippet } from "@/api/scripts";
import { notifySuccess } from "@/utils/notify";
@@ -141,13 +128,6 @@ export default {
// setup quasar plugins
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
// setup quasar
const $q = useQuasar();
// setup store
const store = useStore();
const openAIEnabled = computed(() => store.state.openAIIntegrationEnabled);
// snippet form logic
const snippet = props.snippet
? ref(Object.assign({}, props.snippet))
@@ -187,23 +167,6 @@ export default {
loading.value = false;
}
function generateScriptOpenAI() {
$q.dialog({
title: "Ask ChatGPT what you need!",
prompt: {
model: `${lang.value} code that `,
type: "text",
},
cancel: true,
persistent: true,
}).onOk(async (data) => {
const completion = await generateScript({
prompt: data,
});
snippet.value.code = completion;
});
}
return {
// reactive data
formSnippet: snippet.value,
@@ -216,11 +179,9 @@ export default {
//computed
title,
openAIEnabled,
//methods
submitForm,
generateScriptOpenAI,
// quasar dialog plugin
dialogRef,

View File

@@ -93,20 +93,6 @@
/>
</q-card-section>
<q-card-section>
<tactical-dropdown
v-model="script.env_vars"
label="Environment Variables"
placeholder="(press Enter after typing each key=value pair)"
filled
use-input
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<q-input
label="Default Timeout"

View File

@@ -45,7 +45,6 @@ export default {
args: props.script.args,
shell: props.script.shell,
run_as_user: props.script.run_as_user,
env_vars: props.script.env_vars,
};
try {
ret.value = await testScript(props.agent, data);

View File

@@ -102,7 +102,7 @@
<tactical-dropdown
v-if="actionType === 'script'"
class="col-3"
class="col-4"
label="Select script"
v-model="script"
:options="scriptOptions"
@@ -113,7 +113,7 @@
<q-select
v-if="actionType === 'script'"
class="col-3"
class="col-5"
dense
label="Script Arguments (press Enter after typing each argument)"
filled
@@ -126,21 +126,6 @@
new-value-mode="add"
/>
<q-select
v-if="actionType === 'script'"
class="col-3"
dense
:label="envVarsLabel"
filled
v-model="defaultEnvVars"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-input
v-if="actionType === 'script'"
class="col-2"
@@ -225,9 +210,6 @@
<q-item-label caption>
Arguments: {{ element.script_args }}
</q-item-label>
<q-item-label caption>
Env Vars: {{ element.env_vars }}
</q-item-label>
<q-item-label caption>
Timeout: {{ element.timeout }}
</q-item-label>
@@ -745,7 +727,6 @@ import { useCheckDropdown } from "@/composables/checks";
import { useCustomFieldDropdown } from "@/composables/core";
import { notifySuccess, notifyError } from "@/utils/notify";
import { validateTimePeriod } from "@/utils/validation";
import { envVarsLabel } from "@/constants/constants";
import {
convertPeriodToSeconds,
convertToBitArray,
@@ -836,15 +817,10 @@ export default {
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
// setup dropdowns
const {
script,
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
} = useScriptDropdown(undefined, {
onMount: true,
});
const { script, scriptOptions, defaultTimeout, defaultArgs } =
useScriptDropdown(undefined, {
onMount: true,
});
// set defaultTimeout to 30
defaultTimeout.value = 30;
@@ -938,7 +914,6 @@ export default {
script: script.value,
timeout: defaultTimeout.value,
script_args: defaultArgs.value,
env_vars: defaultEnvVars.value,
});
} else if (actionType.value === "cmd") {
task.value.actions.push({
@@ -952,7 +927,6 @@ export default {
// clear fields after add
script.value = null;
defaultArgs.value = [];
defaultEnvVars.value = [];
defaultTimeout.value = 30;
command.value = "";
}
@@ -1115,7 +1089,6 @@ export default {
script,
defaultTimeout,
defaultArgs,
defaultEnvVars,
actionType,
command,
shell,
@@ -1143,7 +1116,6 @@ export default {
monthOptions,
taskTypeOptions,
taskInstancePolicyOptions,
envVarsLabel,
// methods
submit,

View File

@@ -1,5 +1,4 @@
import { computed, ref } from "vue";
import { useStore } from "vuex";
import { ref } from "vue";
import { fetchAgents } from "@/api/agents";
import { formatAgentOptions } from "@/utils/format";
@@ -29,12 +28,10 @@ export function useAgentDropdown() {
}
export function cmdPlaceholder(shell) {
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;
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 /";
}
export const agentPlatformOptions = [

View File

@@ -8,7 +8,6 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
const scriptOptions = ref([]);
const defaultTimeout = ref(30);
const defaultArgs = ref([]);
const defaultEnvVars = ref([]);
const script = ref(setScript);
const syntax = ref("");
const link = ref("");
@@ -30,7 +29,6 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
);
defaultTimeout.value = tmpScript.timeout;
defaultArgs.value = tmpScript.args;
defaultEnvVars.value = tmpScript.env_vars;
syntax.value = tmpScript.syntax;
link.value =
tmpScript.script_type === "builtin"
@@ -51,7 +49,6 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
syntax,
link,

View File

@@ -1,10 +1,15 @@
export const GOARCH_AMD64 = "amd64";
export const GOARCH_i386 = "386";
export const GOARCH_ARM64 = "arm64";
export const GOARCH_ARM32 = "arm";
const GOARCH_AMD64 = "amd64";
const GOARCH_i386 = "386";
const GOARCH_ARM64 = "arm64";
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.";
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. Not supported on Windows Server.";
export const envVarsLabel =
"Environment vars (press Enter after typing each key=value pair)";
export {
GOARCH_AMD64,
GOARCH_i386,
GOARCH_ARM64,
GOARCH_ARM32,
runAsUserToolTip,
};

View File

@@ -19,15 +19,10 @@
inline-actions
class="bg-yellow text-black text-center"
>
<q-icon size="xl" name="warning" />
<span
><br />Your code signing token is no longer valid.<br /><br />
If you have downgraded or cancelled your sponsorship, please delete
your token from the Code Signing modal and refresh to get rid of this
banner.<br /><br />
For any issues or to renew your sponsorship please email
support@amidaware.com<br /><br
/></span>
<q-icon size="xl" name="warning" />
<span><br />Your code signing token is no longer valid.<br/><br/>
If you have downgraded or cancelled your sponsorship, please delete your token from the Code Signing modal and refresh to get rid of this banner.<br/><br/>
For any issues or to renew your sponsorship please email support@amidaware.com<br/><br/></span>
<q-btn
color="dark"
icon="refresh"
@@ -56,27 +51,15 @@
Tactical RMM<span class="text-overline q-ml-sm"
>v{{ currentTRMMVersion }}</span
>
<!-- 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-chip
>
<!-- cert expiring soon check -->
<q-chip
v-if="daysUntilCertExpires <= 15"
dense
:color="dash_negative_color"
text-color="black"
icon="warning"
>Certificate expires in {{ daysUntilCertExpires }} days</q-chip
<span class="text-overline q-ml-md" v-if="updateAvailable()"
><q-badge color="warning"
><a :href="latestReleaseURL" target="_blank"
>v{{ latestTRMMVersion }} available</a
></q-badge
></span
>
</q-toolbar-title>
<!-- temp dark mode toggle -->
<q-toggle
v-model="darkMode"
@@ -106,11 +89,7 @@
</q-item>
<q-item>
<q-item-section avatar>
<q-icon
name="power_off"
size="sm"
:color="dash_negative_color"
/>
<q-icon name="power_off" size="sm" color="negative" />
</q-item-section>
<q-item-section no-wrap>
@@ -129,11 +108,7 @@
</q-item>
<q-item>
<q-item-section avatar>
<q-icon
name="power_off"
size="sm"
:color="dash_negative_color"
/>
<q-icon name="power_off" size="sm" color="negative" />
</q-item-section>
<q-item-section no-wrap>
@@ -160,32 +135,6 @@
<q-item-label>Preferences</q-item-label>
</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>Account</q-item-section>
<q-item-section side>
<q-icon name="keyboard_arrow_right" />
</q-item-section>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item
clickable
v-ripple
@click="resetPassword"
v-close-popup
>
<q-item-section>
<q-item-label>Reset Password</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-ripple @click="reset2FA" v-close-popup>
<q-item-section>
<q-item-label>Reset 2FA</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-item>
<q-item to="/expired" exact>
<q-item-section>
<q-item-label>Logout</q-item-label>
@@ -207,13 +156,10 @@ import { useQuasar } from "quasar";
import { useStore } from "vuex";
import axios from "axios";
import { getWSUrl } from "@/websocket/channels";
import { resetTwoFactor } from "@/api/accounts";
import { notifySuccess } from "@/utils/notify";
// ui imports
import AlertsIcon from "@/components/AlertsIcon.vue";
import UserPreferences from "@/components/modals/coresettings/UserPreferences.vue";
import ResetPass from "@/components/accounts/ResetPass.vue";
export default {
name: "MainLayout",
@@ -238,8 +184,6 @@ 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
@@ -253,31 +197,10 @@ export default {
}).onOk(() => store.dispatch("getDashInfo"));
}
function resetPassword() {
$q.dialog({
component: ResetPass,
});
}
function reset2FA() {
$q.dialog({
title: "Reset 2FA",
message: "Are you sure you would like to reset your 2FA token?",
cancel: true,
persistent: true,
}).onOk(async () => {
try {
const ret = await resetTwoFactor();
notifySuccess(ret, 3000);
} catch {}
});
}
const serverCount = ref(0);
const serverOfflineCount = ref(0);
const workstationCount = ref(0);
const workstationOfflineCount = ref(0);
const daysUntilCertExpires = ref(100);
const ws = ref(null);
@@ -285,13 +208,6 @@ 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);
@@ -304,7 +220,6 @@ 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 {
@@ -328,18 +243,13 @@ export default {
poll.value = setInterval(() => {
store.dispatch("checkVer");
store.dispatch("getDashInfo", false);
}, 60 * 4 * 1000);
}, 60 * 5 * 1000);
}
const updateAvailable = computed(() => {
if (
latestTRMMVersion.value === "error" ||
hosted.value ||
currentTRMMVersion.value?.includes("-dev")
)
return false;
function updateAvailable() {
if (latestTRMMVersion.value === "error" || hosted.value) return false;
return currentTRMMVersion.value !== latestTRMMVersion.value;
});
}
onMounted(() => {
setupWS();
@@ -360,7 +270,6 @@ export default {
serverOfflineCount,
workstationCount,
workstationOfflineCount,
daysUntilCertExpires,
latestReleaseURL,
currentTRMMVersion,
latestTRMMVersion,
@@ -369,13 +278,9 @@ export default {
darkMode,
hosted,
tokenExpired,
dash_warning_color,
dash_negative_color,
// methods
showUserPreferences,
resetPassword,
reset2FA,
updateAvailable,
};
},

View File

@@ -193,7 +193,6 @@ export default {
value: script.id,
timeout: script.default_timeout,
args: script.args,
env_vars: script.env_vars,
});
} else if (cat === "Unassigned" && !script.category) {
tmp.push({
@@ -201,7 +200,6 @@ export default {
value: script.id,
timeout: script.default_timeout,
args: script.args,
env_vars: script.env_vars,
});
}
});

View File

@@ -33,16 +33,6 @@ export default function () {
currentTRMMVersion: null,
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: {
@@ -146,24 +136,6 @@ export default function () {
setDateFormat(state, val) {
state.dateFormat = val;
},
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) {
@@ -188,9 +160,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);
@@ -222,111 +194,107 @@ export default function () {
commit("AGENT_TABLE_LOADING", false);
},
async getDashInfo({ commit }, edited = true) {
async getDashInfo(context, 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 });
commit(
context.commit(
"setClearSearchWhenSwitching",
data.clear_search_when_switching
);
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);
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);
}
Dark.set(data.dark_mode);
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);
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);
if (data?.date_format !== "") commit("setDateFormat", data.date_format);
else commit("setDateFormat", data.default_date_format);
if (data.date_format && data.date_format !== "")
context.commit("setDateFormat", data.date_format);
else context.commit("setDateFormat", data.default_date_format);
},
loadTree({ commit, state }) {
setTimeout(() => {
axios
.get("/clients/")
.then((r) => {
if (r.data.length === 0) {
this.$router.push({ name: "InitialSetup" });
}
axios
.get("/clients/")
.then((r) => {
if (r.data.length === 0) {
this.$router.push({ name: "InitialSetup" });
}
let output = [];
for (let client of r.data) {
let childSites = [];
for (let site of client.sites) {
let siteNode = {
label: site.name,
id: site.id,
raw: `Site|${site.id}`,
header: "generic",
icon: "apartment",
selectable: true,
site: site,
};
if (site.maintenance_mode) {
siteNode["color"] = "green";
} else if (site.failing_checks.error) {
siteNode["color"] = "negative";
} else if (site.failing_checks.warning) {
siteNode["color"] = "warning";
}
childSites.push(siteNode);
}
let clientNode = {
label: client.name,
id: client.id,
raw: `Client|${client.id}`,
header: "root",
icon: "business",
children: childSites,
client: client,
let output = [];
for (let client of r.data) {
let childSites = [];
for (let site of client.sites) {
let siteNode = {
label: site.name,
id: site.id,
raw: `Site|${site.id}`,
header: "generic",
icon: "apartment",
selectable: true,
site: site,
};
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";
if (site.maintenance_mode) {
siteNode["color"] = "green";
} else if (site.failing_checks.error) {
siteNode["color"] = "negative";
} else if (site.failing_checks.warning) {
siteNode["color"] = "warning";
}
output.push(clientNode);
childSites.push(siteNode);
}
const sorted = output.sort((a, b) =>
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);
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";
}
})
.catch(() => {
state.treeReady = true;
});
}, 150);
output.push(clientNode);
}
const sorted = output.sort((a, b) =>
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) {
axios.get("/core/version/").then((r) => {

View File

@@ -68,7 +68,6 @@ export function formatScriptOptions(data) {
value: script.id,
timeout: script.default_timeout,
args: script.args,
env_vars: script.env_vars,
filename: script.filename,
syntax: script.syntax,
script_type: script.script_type,
@@ -81,7 +80,6 @@ export function formatScriptOptions(data) {
value: script.id,
timeout: script.default_timeout,
args: script.args,
env_vars: script.env_vars,
filename: script.filename,
syntax: script.syntax,
script_type: script.script_type,

View File

@@ -173,18 +173,6 @@
</q-menu>
</q-item>
<!-- Bulk Run Checks -->
<q-item
clickable
v-close-popup
@click="runChecks(props.node)"
>
<q-item-section side>
<q-icon name="fas fa-check-double" />
</q-item-section>
<q-item-section>Run Checks</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup>
@@ -452,7 +440,7 @@ export default {
showInstallAgentModal: false,
sitePk: null,
innerModel: (this.$q.screen.height - 82) / 2,
search: this.$route.query.search ? this.$route.query.search : "",
search: "",
filterTextLength: 0,
filterAvailability: "all",
filterPatchesPending: false,
@@ -702,17 +690,6 @@ export default {
})
.onOk(() => this.$store.dispatch("refreshDashboard"));
},
runChecks(node) {
const target = node.children ? "client" : "site";
this.$axios
.post(`/checks/${target}/${node.id}/csbulkrun/`)
.then((r) => {
this.notifySuccess(r.data);
})
.catch((e) => {
console.error(e);
});
},
showToggleMaintenance(node) {
let data = {
id: node.id,

View File

@@ -15,7 +15,7 @@
@click="restartMeshService"
/>
<q-btn
:color="dash_negative_color"
color="negative"
size="sm"
label="Recover Connection"
icon="fas fa-first-aid"
@@ -35,7 +35,6 @@
<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";
@@ -48,17 +47,12 @@ 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();
@@ -70,19 +64,14 @@ export default {
const statusColor = computed(() => {
switch (status.value) {
case "online":
return dash_positive_color.value;
return "positive";
case "offline":
return dash_warning_color.value;
return "warning";
default:
return dash_negative_color.value;
return "negative";
}
});
// TODO refactor this so we're not calling the api twice
const dashInfo = () => {
store.dispatch("getDashInfo", false);
};
async function getMeshURLs() {
$q.loading.show();
try {
@@ -142,7 +131,6 @@ export default {
control,
status,
statusColor,
dash_negative_color,
// methods
repairMeshCentral,