Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2591c9e7d | ||
|
|
69403def2a | ||
|
|
3fdd8272f6 | ||
|
|
339227bedc | ||
|
|
17c7c95cc1 | ||
|
|
a3ceb5e81b | ||
|
|
679d8cab77 | ||
|
|
c4c1474e09 | ||
|
|
82677b0b82 | ||
|
|
b78af07f11 | ||
|
|
24acef19c5 | ||
|
|
fee6edb39e | ||
|
|
89e7db905d | ||
|
|
827e81dcda | ||
|
|
6ea3a053f2 | ||
|
|
88d297f7c6 | ||
|
|
6c57d3e6b1 | ||
|
|
7fcbe6fbd8 | ||
|
|
0113fbc761 | ||
|
|
95df8c1889 | ||
|
|
819a364207 | ||
|
|
ed2b07fb0b | ||
|
|
64ed5e8740 | ||
|
|
a2f472ef9c | ||
|
|
cdeaa3d9c4 | ||
|
|
8c6ac164ba | ||
|
|
dc68b16ff2 | ||
|
|
a4f15fd05a | ||
|
|
176675abd8 | ||
|
|
73dc278ac4 | ||
|
|
d6b443296b | ||
|
|
f3c718d29c | ||
|
|
5955af08c7 | ||
|
|
dec1ccc98a | ||
|
|
a78780b837 | ||
|
|
beff8eb10e | ||
|
|
8403ac0e93 | ||
|
|
c2f21b70dd | ||
|
|
520145e0e3 | ||
|
|
6a132187a2 | ||
|
|
a63a9ccd76 | ||
|
|
ff1eb791db | ||
|
|
13bd88b979 | ||
|
|
5b0c244920 | ||
|
|
0318a17cac | ||
|
|
b7a91563b0 | ||
|
|
75296ed8ee | ||
|
|
09bee45b2f | ||
|
|
3573c48872 | ||
|
|
784841c221 | ||
|
|
ed788a1861 | ||
|
|
ab19afca16 | ||
|
|
bd6b08505a | ||
|
|
acd64f25f2 | ||
|
|
087be2c232 | ||
|
|
91a3272843 | ||
|
|
6e64f0a11b | ||
|
|
8f34f76a1d | ||
|
|
f24c6a7a80 | ||
|
|
d87861c212 | ||
|
|
5f56e7017b | ||
|
|
9c033c1c90 | ||
|
|
ba14ed348e | ||
|
|
99490bf859 | ||
|
|
7e25db6622 | ||
|
|
78636c436f | ||
|
|
72cdeeaa6a | ||
|
|
d37122386f | ||
|
|
17d960fca9 | ||
|
|
d2e0b8ad9b |
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -5,7 +5,7 @@
|
|||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"editorconfig.editorconfig",
|
"editorconfig.editorconfig",
|
||||||
"vue.volar",
|
"vue.volar",
|
||||||
"wayou.vscode-todo-highlight",
|
"wayou.vscode-todo-highlight"
|
||||||
],
|
],
|
||||||
"unwantedRecommendations": [
|
"unwantedRecommendations": [
|
||||||
"octref.vetur",
|
"octref.vetur",
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -4,7 +4,7 @@
|
|||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"[vue][javascript][typescript][javascriptreact]": {
|
"[vue][javascript][typescript][javascriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
"editor.codeActionsOnSave": ["source.fixAll.eslint"]
|
||||||
},
|
},
|
||||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
|
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"**/node_modules/": true,
|
"**/node_modules/": true,
|
||||||
"/node_modules/**": true,
|
"/node_modules/**": true,
|
||||||
"**/env/": true,
|
"**/env/": true,
|
||||||
"/env/**": true,
|
"/env/**": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
index.html
36
index.html
@@ -1,24 +1,22 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= productName %></title>
|
||||||
|
|
||||||
<head>
|
<meta charset="utf-8" />
|
||||||
<title>
|
<meta name="robots" content="noindex" />
|
||||||
<%= productName %>
|
<meta name="description" content="<%= productDescription %>" />
|
||||||
</title>
|
<meta name="format-detection" content="telephone=no" />
|
||||||
|
<meta name="msapplication-tap-highlight" content="no" />
|
||||||
<meta charset="utf-8" />
|
<meta
|
||||||
<meta name="robots" content="noindex" />
|
name="viewport"
|
||||||
<meta name="description" content="<%= productDescription %>" />
|
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<% } %>"
|
||||||
<meta name="format-detection" content="telephone=no" />
|
/>
|
||||||
<meta name="msapplication-tap-highlight" content="no" />
|
<link rel="icon" type="image/ico" href="favicon.ico" />
|
||||||
<meta name="viewport"
|
<script src="/env-config.js"></script>
|
||||||
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<% } %>" />
|
</head>
|
||||||
<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>
|
</html>
|
||||||
|
|||||||
7093
package-lock.json
generated
7093
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
40
package.json
40
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "web",
|
"name": "web",
|
||||||
"version": "0.101.3",
|
"version": "0.101.22",
|
||||||
"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.15.5",
|
"@quasar/extras": "1.16.4",
|
||||||
"apexcharts": "3.35.5",
|
"apexcharts": "3.40.0",
|
||||||
"axios": "0.27.2",
|
"axios": "1.4.0",
|
||||||
"dotenv": "16.0.3",
|
"dotenv": "16.0.3",
|
||||||
"qrcode.vue": "3.3.3",
|
"qrcode.vue": "3.4.0",
|
||||||
"quasar": "2.10.0",
|
"quasar": "2.12.0",
|
||||||
"vue": "3.2.41",
|
"vue": "3.2.47",
|
||||||
"vue3-ace-editor": "2.2.2",
|
"vue3-ace-editor": "2.2.2",
|
||||||
"vue3-apexcharts": "1.4.1",
|
"vue3-apexcharts": "1.4.1",
|
||||||
"vuedraggable": "4.1.0",
|
"vuedraggable": "4.1.0",
|
||||||
"vue-router": "4.1.5",
|
"vue-router": "4.1.6",
|
||||||
"vuex": "4.1.0"
|
"vuex": "4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@quasar/cli": "^1.3.2",
|
"@quasar/cli": "^2.2.1",
|
||||||
"@intlify/vite-plugin-vue-i18n": "^6.0.3",
|
"@intlify/unplugin-vue-i18n": "^0.10.0",
|
||||||
"@quasar/app-vite": "^1.1.3",
|
"@quasar/app-vite": "^1.4.3",
|
||||||
"@types/node": "^18.11.2",
|
"@types/node": "^20.2.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||||
"@typescript-eslint/parser": "^5.40.1",
|
"@typescript-eslint/parser": "^5.59.7",
|
||||||
"autoprefixer": "^10.4.12",
|
"autoprefixer": "10.4.14",
|
||||||
"eslint": "^8.25.0",
|
"eslint": "8.41.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "8.8.0",
|
||||||
"eslint-plugin-vue": "^8.5.0",
|
"eslint-plugin-vue": "8.7.1",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "2.8.8",
|
||||||
"typescript": "^4.8.4"
|
"typescript": "5.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,18 +4,18 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
// https://github.com/postcss/autoprefixer
|
// https://github.com/postcss/autoprefixer
|
||||||
require('autoprefixer')({
|
require("autoprefixer")({
|
||||||
overrideBrowserslist: [
|
overrideBrowserslist: [
|
||||||
'last 4 Chrome versions',
|
"last 4 Chrome versions",
|
||||||
'last 4 Firefox versions',
|
"last 4 Firefox versions",
|
||||||
'last 4 Edge versions',
|
"last 4 Edge versions",
|
||||||
'last 4 Safari versions',
|
"last 4 Safari versions",
|
||||||
'last 4 Android versions',
|
"last 4 Android versions",
|
||||||
'last 4 ChromeAndroid versions',
|
"last 4 ChromeAndroid versions",
|
||||||
'last 4 FirefoxAndroid versions',
|
"last 4 FirefoxAndroid versions",
|
||||||
'last 4 iOS versions'
|
"last 4 iOS versions",
|
||||||
]
|
],
|
||||||
})
|
}),
|
||||||
|
|
||||||
// https://github.com/elchininet/postcss-rtlcss
|
// https://github.com/elchininet/postcss-rtlcss
|
||||||
// If you want to support RTL css, then
|
// 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
|
// 2. optionally set quasar.config.js > framework > lang to an RTL language
|
||||||
// 3. uncomment the following line:
|
// 3. uncomment the following line:
|
||||||
// require('postcss-rtlcss')
|
// require('postcss-rtlcss')
|
||||||
]
|
],
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -12,6 +12,25 @@ export async function fetchUsers(params = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resetPass(pass) {
|
||||||
|
const payload = { password: pass };
|
||||||
|
try {
|
||||||
|
const { data } = await axios.put(`${baseUrl}/resetpw/`, payload);
|
||||||
|
return data;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetTwoFactor() {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.put(`${baseUrl}/reset2fa/`);
|
||||||
|
return data;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// role api function
|
// role api function
|
||||||
export async function fetchRoles(params = {}) {
|
export async function fetchRoles(params = {}) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,3 +38,8 @@ export async function runURLAction(payload) {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function generateScript(payload) {
|
||||||
|
const { data } = await axios.post(`${baseUrl}/openai/generate/`, payload);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,17 +378,13 @@ export default {
|
|||||||
"local_ips",
|
"local_ips",
|
||||||
"make_model",
|
"make_model",
|
||||||
"physical_disks",
|
"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
|
// quasar filter only does visible columns so this is a hack to add hidden columns we want to filter
|
||||||
for (const elem of hiddenFields) {
|
// originally I was modifying cols directly but this led to phantom colum so doing it this way now
|
||||||
if (!cols.find((o) => o.name === elem)) {
|
// https://github.com/amidaware/tacticalrmm/issues/1264
|
||||||
cols.push({
|
const allColumns = [...cols, ...hiddenFields.map((field) => ({ field }))];
|
||||||
name: elem,
|
|
||||||
field: elem,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const lowerTerms = terms ? terms.toLowerCase() : "";
|
const lowerTerms = terms ? terms.toLowerCase() : "";
|
||||||
let advancedFilter = false;
|
let advancedFilter = false;
|
||||||
@@ -437,8 +438,12 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal text filter
|
// Normal text filter
|
||||||
return cols.some((col) => {
|
return allColumns.some((col) => {
|
||||||
const val = cellValue(col, row) + "";
|
let valObj = cellValue(col, row);
|
||||||
|
if (Array.isArray(valObj)) {
|
||||||
|
valObj = valObj.map((item) => (item.value ? item.value : item));
|
||||||
|
}
|
||||||
|
const val = valObj + "";
|
||||||
const haystack =
|
const haystack =
|
||||||
val === "undefined" || val === "null" ? "" : val.toLowerCase();
|
val === "undefined" || val === "null" ? "" : val.toLowerCase();
|
||||||
return haystack.indexOf(search) !== -1;
|
return haystack.indexOf(search) !== -1;
|
||||||
@@ -489,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,
|
||||||
@@ -533,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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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+";
|
||||||
|
|||||||
75
src/components/accounts/ResetPass.vue
Normal file
75
src/components/accounts/ResetPass.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<q-card class="q-dialog-plugin" style="width: 60vw">
|
||||||
|
<q-card-section class="row">
|
||||||
|
<div class="col-3">New password:</div>
|
||||||
|
<div class="col-9">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
v-model="pass"
|
||||||
|
:type="isPwd ? 'password' : 'text'"
|
||||||
|
:rules="[(val) => !!val || '*Required']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="isPwd ? 'visibility_off' : 'visibility'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="isPwd = !isPwd"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">Confirm password:</div>
|
||||||
|
<div class="col-9">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
v-model="pass2"
|
||||||
|
:type="isPwd ? 'password' : 'text'"
|
||||||
|
:rules="[(val) => val === pass || 'Passwords do not match']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="isPwd ? 'visibility_off' : 'visibility'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="isPwd = !isPwd"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
label="Reset"
|
||||||
|
@click="onOKClick"
|
||||||
|
:disable="!pass || pass !== pass2"
|
||||||
|
/>
|
||||||
|
<q-btn color="negative" label="Cancel" @click="onDialogCancel" />
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useDialogPluginComponent } from "quasar";
|
||||||
|
import { resetPass } from "@/api/accounts";
|
||||||
|
import { notifySuccess } from "@/utils/notify";
|
||||||
|
|
||||||
|
const pass = ref("");
|
||||||
|
const pass2 = ref("");
|
||||||
|
const isPwd = ref(true);
|
||||||
|
|
||||||
|
defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
||||||
|
useDialogPluginComponent();
|
||||||
|
|
||||||
|
async function onOKClick() {
|
||||||
|
const ret = await resetPass(pass.value);
|
||||||
|
notifySuccess(ret);
|
||||||
|
onDialogOK();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -301,7 +301,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 +311,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 +319,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 +327,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>
|
||||||
@@ -479,6 +479,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();
|
||||||
@@ -653,6 +657,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,
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export default {
|
|||||||
type: "textarea",
|
type: "textarea",
|
||||||
isValid: (val) => !!val,
|
isValid: (val) => !!val,
|
||||||
},
|
},
|
||||||
style: "width: 30vw; max-width: 50vw;",
|
style: "width: 90vw; max-width: 90vw",
|
||||||
ok: { label: "Add" },
|
ok: { label: "Add" },
|
||||||
cancel: true,
|
cancel: true,
|
||||||
}).onOk(async () => {
|
}).onOk(async () => {
|
||||||
@@ -193,7 +193,7 @@ export default {
|
|||||||
type: "textarea",
|
type: "textarea",
|
||||||
isValid: (val) => !!val,
|
isValid: (val) => !!val,
|
||||||
},
|
},
|
||||||
style: "width: 30vw; max-width: 50vw;",
|
style: "width: 90vw; max-width: 90vw",
|
||||||
ok: { label: "Save" },
|
ok: { label: "Save" },
|
||||||
cancel: true,
|
cancel: true,
|
||||||
}).onOk(async (data) => {
|
}).onOk(async (data) => {
|
||||||
|
|||||||
@@ -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">
|
||||||
• <q-badge color="green"> Maintenance Mode </q-badge>
|
• <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>
|
||||||
@@ -158,6 +192,20 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>No checks</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>
|
||||||
<div class="col-1"></div>
|
<div class="col-1"></div>
|
||||||
<!-- right -->
|
<!-- right -->
|
||||||
@@ -193,6 +241,7 @@ import {
|
|||||||
openAgentWindow,
|
openAgentWindow,
|
||||||
} from "@/api/agents";
|
} from "@/api/agents";
|
||||||
import { notifySuccess } from "@/utils/notify";
|
import { notifySuccess } from "@/utils/notify";
|
||||||
|
import { fetchCustomFields } from "@/api/core";
|
||||||
|
|
||||||
// ui imports
|
// ui imports
|
||||||
import AgentActionMenu from "@/components/agents/AgentActionMenu.vue";
|
import AgentActionMenu from "@/components/agents/AgentActionMenu.vue";
|
||||||
@@ -207,18 +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 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,9 +301,37 @@ export default {
|
|||||||
return ret;
|
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() {
|
async function getSummary() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
summary.value = await fetchAgent(selectedAgent.value);
|
summary.value = await fetchAgent(selectedAgent.value);
|
||||||
|
customFieldsDefinitions.value = await fetchCustomFields();
|
||||||
store.commit("setRefreshSummaryTab", false);
|
store.commit("setRefreshSummaryTab", false);
|
||||||
store.commit("setAgentPlatform", summary.value.plat);
|
store.commit("setAgentPlatform", summary.value.plat);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -246,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();
|
||||||
@@ -277,9 +371,16 @@ export default {
|
|||||||
return {
|
return {
|
||||||
// reactive data
|
// reactive data
|
||||||
summary,
|
summary,
|
||||||
|
customFields,
|
||||||
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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -8,16 +8,16 @@
|
|||||||
v
|
v
|
||||||
}}</q-badge>
|
}}</q-badge>
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="!!v"
|
v-if="!!v"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="q-ml-xs"
|
class="q-ml-xs"
|
||||||
flat
|
flat
|
||||||
round
|
round
|
||||||
icon="content_copy"
|
icon="content_copy"
|
||||||
@click="copyValueToClip(v)"
|
@click="copyValueToClip(v)"
|
||||||
>
|
>
|
||||||
<q-tooltip>Copy to Clipboard</q-tooltip>
|
<q-tooltip>Copy to Clipboard</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-separator v-if="info.length > 1" />
|
<q-separator v-if="info.length > 1" />
|
||||||
@@ -42,10 +42,9 @@ export default {
|
|||||||
const tabHeight = computed(() => store.state.tabHeight);
|
const tabHeight = computed(() => store.state.tabHeight);
|
||||||
|
|
||||||
function copyValueToClip(val) {
|
function copyValueToClip(val) {
|
||||||
copyToClipboard(val)
|
copyToClipboard(val).then(() => {
|
||||||
.then(() => {
|
notifySuccess("Copied to clipboard");
|
||||||
notifySuccess("Copied to clipboard");
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -39,6 +39,19 @@
|
|||||||
new-value-mode="add"
|
new-value-mode="add"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</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>
|
<q-card-section>
|
||||||
<tactical-dropdown
|
<tactical-dropdown
|
||||||
label="Informational return codes (press Enter after typing each code)"
|
label="Informational return codes (press Enter after typing each code)"
|
||||||
@@ -115,6 +128,7 @@ import { useDialogPluginComponent } from "quasar";
|
|||||||
import { useCheckModal } from "@/composables/checks";
|
import { useCheckModal } from "@/composables/checks";
|
||||||
import { useScriptDropdown } from "@/composables/scripts";
|
import { useScriptDropdown } from "@/composables/scripts";
|
||||||
import { validateRetcode } from "@/utils/validation";
|
import { validateRetcode } from "@/utils/validation";
|
||||||
|
import { envVarsLabel } from "@/constants/constants";
|
||||||
|
|
||||||
// ui imports
|
// ui imports
|
||||||
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
|
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
|
||||||
@@ -132,10 +146,15 @@ export default {
|
|||||||
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
||||||
|
|
||||||
// setup script dropdown
|
// setup script dropdown
|
||||||
const { script, scriptOptions, defaultTimeout, defaultArgs } =
|
const {
|
||||||
useScriptDropdown(props.check ? props.check.script : undefined, {
|
script,
|
||||||
onMount: true,
|
scriptOptions,
|
||||||
});
|
defaultTimeout,
|
||||||
|
defaultArgs,
|
||||||
|
defaultEnvVars,
|
||||||
|
} = useScriptDropdown(props.check ? props.check.script : undefined, {
|
||||||
|
onMount: true,
|
||||||
|
});
|
||||||
|
|
||||||
// check logic
|
// check logic
|
||||||
const { state, loading, submit, failOptions, severityOptions } =
|
const { state, loading, submit, failOptions, severityOptions } =
|
||||||
@@ -145,6 +164,7 @@ export default {
|
|||||||
...props.parent,
|
...props.parent,
|
||||||
script,
|
script,
|
||||||
script_args: defaultArgs,
|
script_args: defaultArgs,
|
||||||
|
env_vars: defaultEnvVars,
|
||||||
timeout: defaultTimeout,
|
timeout: defaultTimeout,
|
||||||
check_type: "script",
|
check_type: "script",
|
||||||
fails_b4_alert: 1,
|
fails_b4_alert: 1,
|
||||||
@@ -163,6 +183,7 @@ export default {
|
|||||||
failOptions,
|
failOptions,
|
||||||
scriptOptions,
|
scriptOptions,
|
||||||
severityOptions,
|
severityOptions,
|
||||||
|
envVarsLabel,
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
submit,
|
submit,
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export default {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const result = props.APIKey
|
const result = props.APIKey
|
||||||
? await editAPIKey(data)
|
? await editAPIKey(data.id, data)
|
||||||
: await saveAPIKey(data);
|
: await saveAPIKey(data);
|
||||||
onDialogOK();
|
onDialogOK();
|
||||||
notifySuccess(result);
|
notifySuccess(result);
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// component lifecycle hooks
|
// component lifecycle hooks
|
||||||
onMounted(getAPIKeys());
|
onMounted(getAPIKeys);
|
||||||
return {
|
return {
|
||||||
// reactive data
|
// reactive data
|
||||||
keys,
|
keys,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -89,7 +89,8 @@
|
|||||||
<p class="text-italic">
|
<p class="text-italic">
|
||||||
Note: the auth token above will be valid for {{ info.expires }} hours.
|
Note: the auth token above will be valid for {{ info.expires }} hours.
|
||||||
</p>
|
</p>
|
||||||
<q-btn v-if="info.plat === 'windows'"
|
<q-btn
|
||||||
|
v-if="info.plat === 'windows'"
|
||||||
type="a"
|
type="a"
|
||||||
:href="info.data.url"
|
:href="info.data.url"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -102,6 +102,18 @@
|
|||||||
new-value-mode="add"
|
new-value-mode="add"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</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'">
|
<q-card-section v-if="mode === 'command'">
|
||||||
<p>Shell</p>
|
<p>Shell</p>
|
||||||
@@ -208,7 +220,7 @@ import { runBulkAction } from "@/api/agents";
|
|||||||
import { notifySuccess } from "@/utils/notify";
|
import { notifySuccess } from "@/utils/notify";
|
||||||
import { cmdPlaceholder } from "@/composables/agents";
|
import { cmdPlaceholder } from "@/composables/agents";
|
||||||
import { removeExtraOptionCategories } from "@/utils/format";
|
import { removeExtraOptionCategories } from "@/utils/format";
|
||||||
import { runAsUserToolTip } from "@/constants/constants";
|
import { envVarsLabel, runAsUserToolTip } from "@/constants/constants";
|
||||||
|
|
||||||
// ui imports
|
// ui imports
|
||||||
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
|
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
|
||||||
@@ -284,6 +296,7 @@ export default {
|
|||||||
scriptOptions,
|
scriptOptions,
|
||||||
defaultTimeout,
|
defaultTimeout,
|
||||||
defaultArgs,
|
defaultArgs,
|
||||||
|
defaultEnvVars,
|
||||||
getScriptOptions,
|
getScriptOptions,
|
||||||
} = useScriptDropdown();
|
} = useScriptDropdown();
|
||||||
const { agents, agentOptions, getAgentOptions } = useAgentDropdown();
|
const { agents, agentOptions, getAgentOptions } = useAgentDropdown();
|
||||||
@@ -307,6 +320,7 @@ export default {
|
|||||||
script,
|
script,
|
||||||
timeout: defaultTimeout,
|
timeout: defaultTimeout,
|
||||||
args: defaultArgs,
|
args: defaultArgs,
|
||||||
|
env_vars: defaultEnvVars,
|
||||||
run_as_user: false,
|
run_as_user: false,
|
||||||
});
|
});
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -404,6 +418,7 @@ export default {
|
|||||||
targetOptions,
|
targetOptions,
|
||||||
patchModeOptions,
|
patchModeOptions,
|
||||||
runAsUserToolTip,
|
runAsUserToolTip,
|
||||||
|
envVarsLabel,
|
||||||
|
|
||||||
//computed
|
//computed
|
||||||
modalTitle,
|
modalTitle,
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -77,6 +77,18 @@
|
|||||||
new-value-mode="add"
|
new-value-mode="add"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</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-card-section>
|
||||||
<q-option-group
|
<q-option-group
|
||||||
v-model="state.output"
|
v-model="state.output"
|
||||||
@@ -178,7 +190,7 @@ import { useScriptDropdown } from "@/composables/scripts";
|
|||||||
import { useCustomFieldDropdown } from "@/composables/core";
|
import { useCustomFieldDropdown } from "@/composables/core";
|
||||||
import { runScript } from "@/api/agents";
|
import { runScript } from "@/api/agents";
|
||||||
import { notifySuccess } from "@/utils/notify";
|
import { notifySuccess } from "@/utils/notify";
|
||||||
import { runAsUserToolTip } from "@/constants/constants";
|
import { envVarsLabel, runAsUserToolTip } from "@/constants/constants";
|
||||||
import {
|
import {
|
||||||
formatScriptSyntax,
|
formatScriptSyntax,
|
||||||
removeExtraOptionCategories,
|
removeExtraOptionCategories,
|
||||||
@@ -209,11 +221,18 @@ export default {
|
|||||||
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||||
|
|
||||||
// setup dropdowns
|
// setup dropdowns
|
||||||
const { script, scriptOptions, defaultTimeout, defaultArgs, syntax, link } =
|
const {
|
||||||
useScriptDropdown(props.script, {
|
script,
|
||||||
onMount: true,
|
scriptOptions,
|
||||||
filterByPlatform: props.agent.plat,
|
defaultTimeout,
|
||||||
});
|
defaultArgs,
|
||||||
|
defaultEnvVars,
|
||||||
|
syntax,
|
||||||
|
link,
|
||||||
|
} = useScriptDropdown(props.script, {
|
||||||
|
onMount: true,
|
||||||
|
filterByPlatform: props.agent.plat,
|
||||||
|
});
|
||||||
const { customFieldOptions } = useCustomFieldDropdown({ onMount: true });
|
const { customFieldOptions } = useCustomFieldDropdown({ onMount: true });
|
||||||
|
|
||||||
// main run script functionaity
|
// main run script functionaity
|
||||||
@@ -225,6 +244,7 @@ export default {
|
|||||||
save_all_output: false,
|
save_all_output: false,
|
||||||
script,
|
script,
|
||||||
args: defaultArgs,
|
args: defaultArgs,
|
||||||
|
env_vars: defaultEnvVars,
|
||||||
timeout: defaultTimeout,
|
timeout: defaultTimeout,
|
||||||
run_as_user: false,
|
run_as_user: false,
|
||||||
});
|
});
|
||||||
@@ -281,6 +301,7 @@ export default {
|
|||||||
// non-reactive data
|
// non-reactive data
|
||||||
outputOptions,
|
outputOptions,
|
||||||
runAsUserToolTip,
|
runAsUserToolTip,
|
||||||
|
envVarsLabel,
|
||||||
|
|
||||||
//methods
|
//methods
|
||||||
formatScriptSyntax,
|
formatScriptSyntax,
|
||||||
|
|||||||
@@ -204,6 +204,20 @@
|
|||||||
new-value-mode="add"
|
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
|
<q-input
|
||||||
class="q-mb-sm"
|
class="q-mb-sm"
|
||||||
label="Failure action timeout (seconds)"
|
label="Failure action timeout (seconds)"
|
||||||
@@ -277,6 +291,20 @@
|
|||||||
new-value-mode="add"
|
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
|
<q-input
|
||||||
class="q-mb-sm"
|
class="q-mb-sm"
|
||||||
label="Resolved action timeout (seconds)"
|
label="Resolved action timeout (seconds)"
|
||||||
@@ -696,9 +724,11 @@ export default {
|
|||||||
is_active: true,
|
is_active: true,
|
||||||
action: null,
|
action: null,
|
||||||
action_args: [],
|
action_args: [],
|
||||||
|
action_env_vars: [],
|
||||||
action_timeout: 15,
|
action_timeout: 15,
|
||||||
resolved_action: null,
|
resolved_action: null,
|
||||||
resolved_action_args: [],
|
resolved_action_args: [],
|
||||||
|
resolved_action_env_vars: [],
|
||||||
resolved_action_timeout: 15,
|
resolved_action_timeout: 15,
|
||||||
email_recipients: [],
|
email_recipients: [],
|
||||||
email_from: "",
|
email_from: "",
|
||||||
@@ -762,11 +792,13 @@ export default {
|
|||||||
(i) => i.value === this.template.action
|
(i) => i.value === this.template.action
|
||||||
);
|
);
|
||||||
this.template.action_args = script.args;
|
this.template.action_args = script.args;
|
||||||
|
this.template.action_env_vars = script.env_vars;
|
||||||
} else if (type === "resolved") {
|
} else if (type === "resolved") {
|
||||||
const script = this.scriptOptions.find(
|
const script = this.scriptOptions.find(
|
||||||
(i) => i.value === this.template.resolved_action
|
(i) => i.value === this.template.resolved_action
|
||||||
);
|
);
|
||||||
this.template.resolved_action_args = script.args;
|
this.template.resolved_action_args = script.args;
|
||||||
|
this.template.resolved_action_env_vars = script.env_vars;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleAddEmail() {
|
toggleAddEmail() {
|
||||||
|
|||||||
@@ -12,6 +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-tabs>
|
</q-tabs>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:after>
|
<template v-slot:after>
|
||||||
@@ -508,6 +509,49 @@
|
|||||||
<q-tab-panel name="apikeys">
|
<q-tab-panel name="apikeys">
|
||||||
<APIKeysTable />
|
<APIKeysTable />
|
||||||
</q-tab-panel>
|
</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-tab-panels>
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
<q-card-section class="row items-center">
|
<q-card-section class="row items-center">
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,17 @@
|
|||||||
:style="maximized ? '' : 'width: 90vw; max-width: 90vw'"
|
:style="maximized ? '' : 'width: 90vw; max-width: 90vw'"
|
||||||
>
|
>
|
||||||
<q-bar>
|
<q-bar>
|
||||||
{{ title }}
|
<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"
|
||||||
|
/>
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-btn
|
<q-btn
|
||||||
dense
|
dense
|
||||||
@@ -57,105 +67,133 @@
|
|||||||
><br />Add one to get rid of this warning. Ignore if windows.
|
><br />Add one to get rid of this warning. Ignore if windows.
|
||||||
</q-banner>
|
</q-banner>
|
||||||
<div class="row q-pa-sm">
|
<div class="row q-pa-sm">
|
||||||
<div class="col-4 q-gutter-sm q-pr-sm">
|
<q-scroll-area
|
||||||
<q-input
|
:thumb-style="{
|
||||||
filled
|
right: '4px',
|
||||||
dense
|
borderRadius: '5px',
|
||||||
:readonly="readonly"
|
width: '5px',
|
||||||
v-model="formScript.name"
|
opacity: 0.75,
|
||||||
label="Name"
|
}"
|
||||||
:rules="[(val) => !!val || '*Required']"
|
:bar-style="{
|
||||||
hide-bottom-space
|
right: '2px',
|
||||||
/>
|
borderRadius: '9px',
|
||||||
<q-input
|
width: '9px',
|
||||||
filled
|
opacity: 0.2,
|
||||||
dense
|
}"
|
||||||
:readonly="readonly"
|
class="col-4 q-mb-none q-pb-none"
|
||||||
v-model="formScript.description"
|
:style="{ height: `${maximized ? '82vh' : '64vh'}` }"
|
||||||
label="Description"
|
>
|
||||||
/>
|
<div class="q-gutter-sm q-pr-sm">
|
||||||
<q-select
|
<q-input
|
||||||
:readonly="readonly"
|
filled
|
||||||
options-dense
|
dense
|
||||||
filled
|
:readonly="readonly"
|
||||||
dense
|
v-model="formScript.name"
|
||||||
v-model="formScript.shell"
|
label="Name"
|
||||||
:options="shellOptions"
|
:rules="[(val) => !!val || '*Required']"
|
||||||
emit-value
|
hide-bottom-space
|
||||||
map-options
|
/>
|
||||||
label="Shell Type"
|
<q-input
|
||||||
/>
|
filled
|
||||||
<tactical-dropdown
|
dense
|
||||||
v-model="formScript.supported_platforms"
|
:readonly="readonly"
|
||||||
:options="agentPlatformOptions"
|
v-model="formScript.description"
|
||||||
label="Supported Platforms (All supported if blank)"
|
label="Description"
|
||||||
clearable
|
/>
|
||||||
mapOptions
|
<q-select
|
||||||
filled
|
:readonly="readonly"
|
||||||
multiple
|
options-dense
|
||||||
:readonly="readonly"
|
filled
|
||||||
/>
|
dense
|
||||||
<tactical-dropdown
|
v-model="formScript.shell"
|
||||||
filled
|
:options="shellOptions"
|
||||||
v-model="formScript.category"
|
emit-value
|
||||||
:options="categories"
|
map-options
|
||||||
use-input
|
label="Shell Type"
|
||||||
clearable
|
/>
|
||||||
new-value-mode="add-unique"
|
<tactical-dropdown
|
||||||
filterable
|
v-model="formScript.supported_platforms"
|
||||||
label="Category"
|
:options="agentPlatformOptions"
|
||||||
:readonly="readonly"
|
label="Supported Platforms (All supported if blank)"
|
||||||
hide-bottom-space
|
clearable
|
||||||
/>
|
mapOptions
|
||||||
<tactical-dropdown
|
filled
|
||||||
v-model="formScript.args"
|
multiple
|
||||||
label="Script Arguments (press Enter after typing each argument)"
|
:readonly="readonly"
|
||||||
filled
|
/>
|
||||||
use-input
|
<tactical-dropdown
|
||||||
multiple
|
filled
|
||||||
hide-dropdown-icon
|
v-model="formScript.category"
|
||||||
input-debounce="0"
|
:options="categories"
|
||||||
new-value-mode="add"
|
use-input
|
||||||
:readonly="readonly"
|
clearable
|
||||||
/>
|
new-value-mode="add-unique"
|
||||||
<q-input
|
filterable
|
||||||
type="number"
|
label="Category"
|
||||||
filled
|
:readonly="readonly"
|
||||||
dense
|
hide-bottom-space
|
||||||
:readonly="readonly"
|
/>
|
||||||
v-model.number="formScript.default_timeout"
|
<tactical-dropdown
|
||||||
label="Timeout (seconds)"
|
v-model="formScript.args"
|
||||||
:rules="[(val) => val >= 5 || 'Minimum is 5']"
|
label="Script Arguments (press Enter after typing each argument)"
|
||||||
hide-bottom-space
|
filled
|
||||||
/>
|
use-input
|
||||||
<q-checkbox
|
multiple
|
||||||
v-model="formScript.run_as_user"
|
hide-dropdown-icon
|
||||||
label="Run As User (Windows only)"
|
input-debounce="0"
|
||||||
>
|
new-value-mode="add"
|
||||||
<q-tooltip
|
:readonly="readonly"
|
||||||
>Setting this value on the script model will always override any
|
/>
|
||||||
'Run As User' checkboxes in the UI and force this script to
|
<tactical-dropdown
|
||||||
always be run in the context of the logged in user. If no user
|
v-model="formScript.env_vars"
|
||||||
is logged in, the script will not run and an error will be
|
:label="envVarsLabel"
|
||||||
returned. Not supported on Windows Server.
|
filled
|
||||||
</q-tooltip>
|
use-input
|
||||||
</q-checkbox>
|
multiple
|
||||||
<q-input
|
hide-dropdown-icon
|
||||||
label="Syntax"
|
input-debounce="0"
|
||||||
type="textarea"
|
new-value-mode="add"
|
||||||
style="height: 150px; overflow-y: auto; resize: none"
|
:readonly="readonly"
|
||||||
v-model="formScript.syntax"
|
/>
|
||||||
dense
|
<q-input
|
||||||
filled
|
type="number"
|
||||||
:readonly="readonly"
|
filled
|
||||||
/>
|
dense
|
||||||
</div>
|
: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>
|
||||||
<v-ace-editor
|
<v-ace-editor
|
||||||
v-model:value="formScript.script_body"
|
v-model:value="formScript.script_body"
|
||||||
class="col-8"
|
class="col-8"
|
||||||
:lang="lang"
|
:lang="lang"
|
||||||
:theme="$q.dark.isActive ? 'tomorrow_night_eighties' : 'tomorrow'"
|
:theme="$q.dark.isActive ? 'tomorrow_night_eighties' : 'tomorrow'"
|
||||||
:style="{ height: `${maximized ? '87vh' : '64vh'}` }"
|
:style="{ height: `${maximized ? '82vh' : '64vh'}` }"
|
||||||
wrap
|
wrap
|
||||||
:printMargin="false"
|
:printMargin="false"
|
||||||
:options="{ fontSize: '14px' }"
|
:options="{ fontSize: '14px' }"
|
||||||
@@ -209,9 +247,11 @@
|
|||||||
<script>
|
<script>
|
||||||
// composable imports
|
// composable imports
|
||||||
import { ref, computed, onMounted } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
import { useQuasar, useDialogPluginComponent } from "quasar";
|
import { useQuasar, useDialogPluginComponent } from "quasar";
|
||||||
import { saveScript, editScript, downloadScript } from "@/api/scripts";
|
import { saveScript, editScript, downloadScript } from "@/api/scripts";
|
||||||
import { useAgentDropdown, agentPlatformOptions } from "@/composables/agents";
|
import { useAgentDropdown, agentPlatformOptions } from "@/composables/agents";
|
||||||
|
import { generateScript } from "@/api/core";
|
||||||
import { notifySuccess } from "@/utils/notify";
|
import { notifySuccess } from "@/utils/notify";
|
||||||
|
|
||||||
// ui imports
|
// ui imports
|
||||||
@@ -229,6 +269,7 @@ import "ace-builds/src-noconflict/theme-tomorrow";
|
|||||||
|
|
||||||
// static data
|
// static data
|
||||||
import { shellOptions } from "@/composables/scripts";
|
import { shellOptions } from "@/composables/scripts";
|
||||||
|
import { envVarsLabel } from "@/constants/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ScriptFormModal",
|
name: "ScriptFormModal",
|
||||||
@@ -254,6 +295,10 @@ export default {
|
|||||||
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
||||||
|
// setup store
|
||||||
|
const store = useStore();
|
||||||
|
const openAIEnabled = computed(() => store.state.openAIIntegrationEnabled);
|
||||||
|
|
||||||
// setup agent dropdown
|
// setup agent dropdown
|
||||||
const { agent, agentOptions, getAgentOptions } = useAgentDropdown();
|
const { agent, agentOptions, getAgentOptions } = useAgentDropdown();
|
||||||
|
|
||||||
@@ -266,6 +311,7 @@ export default {
|
|||||||
args: [],
|
args: [],
|
||||||
script_body: "",
|
script_body: "",
|
||||||
run_as_user: false,
|
run_as_user: false,
|
||||||
|
env_vars: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.clone) script.value.name = `(Copy) ${script.value.name}`;
|
if (props.clone) script.value.name = `(Copy) ${script.value.name}`;
|
||||||
@@ -342,6 +388,23 @@ 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
|
// component life cycle hooks
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
agentLoading.value = true;
|
agentLoading.value = true;
|
||||||
@@ -363,13 +426,16 @@ export default {
|
|||||||
// non-reactive data
|
// non-reactive data
|
||||||
shellOptions,
|
shellOptions,
|
||||||
agentPlatformOptions,
|
agentPlatformOptions,
|
||||||
|
envVarsLabel,
|
||||||
|
|
||||||
//computed
|
//computed
|
||||||
title,
|
title,
|
||||||
|
openAIEnabled,
|
||||||
|
|
||||||
//methods
|
//methods
|
||||||
submitForm,
|
submitForm,
|
||||||
openTestScriptModal,
|
openTestScriptModal,
|
||||||
|
generateScriptOpenAI,
|
||||||
|
|
||||||
// quasar dialog plugin
|
// quasar dialog plugin
|
||||||
dialogRef,
|
dialogRef,
|
||||||
|
|||||||
@@ -867,7 +867,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// component life cycle hooks
|
// component life cycle hooks
|
||||||
onMounted(getScripts());
|
onMounted(getScripts);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// reactive data
|
// reactive data
|
||||||
|
|||||||
@@ -11,7 +11,17 @@
|
|||||||
:style="maximized ? '' : 'width: 70vw; max-width: 90vw'"
|
:style="maximized ? '' : 'width: 70vw; max-width: 90vw'"
|
||||||
>
|
>
|
||||||
<q-bar>
|
<q-bar>
|
||||||
{{ title }}
|
<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"
|
||||||
|
/>
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-btn
|
<q-btn
|
||||||
dense
|
dense
|
||||||
@@ -97,6 +107,9 @@
|
|||||||
<script>
|
<script>
|
||||||
// composable imports
|
// composable imports
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import { useQuasar } from "quasar";
|
||||||
|
import { generateScript } from "@/api/core";
|
||||||
import { useDialogPluginComponent } from "quasar";
|
import { useDialogPluginComponent } from "quasar";
|
||||||
import { saveScriptSnippet, editScriptSnippet } from "@/api/scripts";
|
import { saveScriptSnippet, editScriptSnippet } from "@/api/scripts";
|
||||||
import { notifySuccess } from "@/utils/notify";
|
import { notifySuccess } from "@/utils/notify";
|
||||||
@@ -128,6 +141,13 @@ export default {
|
|||||||
// setup quasar plugins
|
// setup quasar plugins
|
||||||
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
||||||
|
|
||||||
|
// setup quasar
|
||||||
|
const $q = useQuasar();
|
||||||
|
|
||||||
|
// setup store
|
||||||
|
const store = useStore();
|
||||||
|
const openAIEnabled = computed(() => store.state.openAIIntegrationEnabled);
|
||||||
|
|
||||||
// snippet form logic
|
// snippet form logic
|
||||||
const snippet = props.snippet
|
const snippet = props.snippet
|
||||||
? ref(Object.assign({}, props.snippet))
|
? ref(Object.assign({}, props.snippet))
|
||||||
@@ -167,6 +187,23 @@ export default {
|
|||||||
loading.value = false;
|
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 {
|
return {
|
||||||
// reactive data
|
// reactive data
|
||||||
formSnippet: snippet.value,
|
formSnippet: snippet.value,
|
||||||
@@ -179,9 +216,11 @@ export default {
|
|||||||
|
|
||||||
//computed
|
//computed
|
||||||
title,
|
title,
|
||||||
|
openAIEnabled,
|
||||||
|
|
||||||
//methods
|
//methods
|
||||||
submitForm,
|
submitForm,
|
||||||
|
generateScriptOpenAI,
|
||||||
|
|
||||||
// quasar dialog plugin
|
// quasar dialog plugin
|
||||||
dialogRef,
|
dialogRef,
|
||||||
|
|||||||
@@ -93,6 +93,20 @@
|
|||||||
/>
|
/>
|
||||||
</q-card-section>
|
</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-card-section>
|
||||||
<q-input
|
<q-input
|
||||||
label="Default Timeout"
|
label="Default Timeout"
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export default {
|
|||||||
args: props.script.args,
|
args: props.script.args,
|
||||||
shell: props.script.shell,
|
shell: props.script.shell,
|
||||||
run_as_user: props.script.run_as_user,
|
run_as_user: props.script.run_as_user,
|
||||||
|
env_vars: props.script.env_vars,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
ret.value = await testScript(props.agent, data);
|
ret.value = await testScript(props.agent, data);
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
|
|
||||||
<tactical-dropdown
|
<tactical-dropdown
|
||||||
v-if="actionType === 'script'"
|
v-if="actionType === 'script'"
|
||||||
class="col-4"
|
class="col-3"
|
||||||
label="Select script"
|
label="Select script"
|
||||||
v-model="script"
|
v-model="script"
|
||||||
:options="scriptOptions"
|
:options="scriptOptions"
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
<q-select
|
<q-select
|
||||||
v-if="actionType === 'script'"
|
v-if="actionType === 'script'"
|
||||||
class="col-5"
|
class="col-3"
|
||||||
dense
|
dense
|
||||||
label="Script Arguments (press Enter after typing each argument)"
|
label="Script Arguments (press Enter after typing each argument)"
|
||||||
filled
|
filled
|
||||||
@@ -126,6 +126,21 @@
|
|||||||
new-value-mode="add"
|
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
|
<q-input
|
||||||
v-if="actionType === 'script'"
|
v-if="actionType === 'script'"
|
||||||
class="col-2"
|
class="col-2"
|
||||||
@@ -210,6 +225,9 @@
|
|||||||
<q-item-label caption>
|
<q-item-label caption>
|
||||||
Arguments: {{ element.script_args }}
|
Arguments: {{ element.script_args }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
|
<q-item-label caption>
|
||||||
|
Env Vars: {{ element.env_vars }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label caption>
|
<q-item-label caption>
|
||||||
Timeout: {{ element.timeout }}
|
Timeout: {{ element.timeout }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
@@ -727,6 +745,7 @@ import { useCheckDropdown } from "@/composables/checks";
|
|||||||
import { useCustomFieldDropdown } from "@/composables/core";
|
import { useCustomFieldDropdown } from "@/composables/core";
|
||||||
import { notifySuccess, notifyError } from "@/utils/notify";
|
import { notifySuccess, notifyError } from "@/utils/notify";
|
||||||
import { validateTimePeriod } from "@/utils/validation";
|
import { validateTimePeriod } from "@/utils/validation";
|
||||||
|
import { envVarsLabel } from "@/constants/constants";
|
||||||
import {
|
import {
|
||||||
convertPeriodToSeconds,
|
convertPeriodToSeconds,
|
||||||
convertToBitArray,
|
convertToBitArray,
|
||||||
@@ -817,10 +836,15 @@ export default {
|
|||||||
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
||||||
|
|
||||||
// setup dropdowns
|
// setup dropdowns
|
||||||
const { script, scriptOptions, defaultTimeout, defaultArgs } =
|
const {
|
||||||
useScriptDropdown(undefined, {
|
script,
|
||||||
onMount: true,
|
scriptOptions,
|
||||||
});
|
defaultTimeout,
|
||||||
|
defaultArgs,
|
||||||
|
defaultEnvVars,
|
||||||
|
} = useScriptDropdown(undefined, {
|
||||||
|
onMount: true,
|
||||||
|
});
|
||||||
|
|
||||||
// set defaultTimeout to 30
|
// set defaultTimeout to 30
|
||||||
defaultTimeout.value = 30;
|
defaultTimeout.value = 30;
|
||||||
@@ -914,6 +938,7 @@ export default {
|
|||||||
script: script.value,
|
script: script.value,
|
||||||
timeout: defaultTimeout.value,
|
timeout: defaultTimeout.value,
|
||||||
script_args: defaultArgs.value,
|
script_args: defaultArgs.value,
|
||||||
|
env_vars: defaultEnvVars.value,
|
||||||
});
|
});
|
||||||
} else if (actionType.value === "cmd") {
|
} else if (actionType.value === "cmd") {
|
||||||
task.value.actions.push({
|
task.value.actions.push({
|
||||||
@@ -927,6 +952,7 @@ export default {
|
|||||||
// clear fields after add
|
// clear fields after add
|
||||||
script.value = null;
|
script.value = null;
|
||||||
defaultArgs.value = [];
|
defaultArgs.value = [];
|
||||||
|
defaultEnvVars.value = [];
|
||||||
defaultTimeout.value = 30;
|
defaultTimeout.value = 30;
|
||||||
command.value = "";
|
command.value = "";
|
||||||
}
|
}
|
||||||
@@ -1089,6 +1115,7 @@ export default {
|
|||||||
script,
|
script,
|
||||||
defaultTimeout,
|
defaultTimeout,
|
||||||
defaultArgs,
|
defaultArgs,
|
||||||
|
defaultEnvVars,
|
||||||
actionType,
|
actionType,
|
||||||
command,
|
command,
|
||||||
shell,
|
shell,
|
||||||
@@ -1116,6 +1143,7 @@ export default {
|
|||||||
monthOptions,
|
monthOptions,
|
||||||
taskTypeOptions,
|
taskTypeOptions,
|
||||||
taskInstancePolicyOptions,
|
taskInstancePolicyOptions,
|
||||||
|
envVarsLabel,
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
submit,
|
submit,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function useUserDropdown(onMount = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (onMount) {
|
if (onMount) {
|
||||||
onMounted(getUserOptions());
|
onMounted(getUserOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
|
|||||||
const scriptOptions = ref([]);
|
const scriptOptions = ref([]);
|
||||||
const defaultTimeout = ref(30);
|
const defaultTimeout = ref(30);
|
||||||
const defaultArgs = ref([]);
|
const defaultArgs = ref([]);
|
||||||
|
const defaultEnvVars = ref([]);
|
||||||
const script = ref(setScript);
|
const script = ref(setScript);
|
||||||
const syntax = ref("");
|
const syntax = ref("");
|
||||||
const link = ref("");
|
const link = ref("");
|
||||||
@@ -29,6 +30,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
|
|||||||
);
|
);
|
||||||
defaultTimeout.value = tmpScript.timeout;
|
defaultTimeout.value = tmpScript.timeout;
|
||||||
defaultArgs.value = tmpScript.args;
|
defaultArgs.value = tmpScript.args;
|
||||||
|
defaultEnvVars.value = tmpScript.env_vars;
|
||||||
syntax.value = tmpScript.syntax;
|
syntax.value = tmpScript.syntax;
|
||||||
link.value =
|
link.value =
|
||||||
tmpScript.script_type === "builtin"
|
tmpScript.script_type === "builtin"
|
||||||
@@ -49,6 +51,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
|
|||||||
scriptOptions,
|
scriptOptions,
|
||||||
defaultTimeout,
|
defaultTimeout,
|
||||||
defaultArgs,
|
defaultArgs,
|
||||||
|
defaultEnvVars,
|
||||||
syntax,
|
syntax,
|
||||||
link,
|
link,
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
const GOARCH_AMD64 = "amd64";
|
export const GOARCH_AMD64 = "amd64";
|
||||||
const GOARCH_i386 = "386";
|
export const GOARCH_i386 = "386";
|
||||||
const GOARCH_ARM64 = "arm64";
|
export const GOARCH_ARM64 = "arm64";
|
||||||
const GOARCH_ARM32 = "arm";
|
export const GOARCH_ARM32 = "arm";
|
||||||
|
|
||||||
const runAsUserToolTip =
|
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. Not supported on Windows Server.";
|
"Run in the context of the logged in user. If no user is logged in, the script will not run and an error will be returned.";
|
||||||
|
|
||||||
export {
|
export const envVarsLabel =
|
||||||
GOARCH_AMD64,
|
"Environment vars (press Enter after typing each key=value pair)";
|
||||||
GOARCH_i386,
|
|
||||||
GOARCH_ARM64,
|
|
||||||
GOARCH_ARM32,
|
|
||||||
runAsUserToolTip,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -19,10 +19,15 @@
|
|||||||
inline-actions
|
inline-actions
|
||||||
class="bg-yellow text-black text-center"
|
class="bg-yellow text-black text-center"
|
||||||
>
|
>
|
||||||
<q-icon size="xl" name="warning" />
|
<q-icon size="xl" name="warning" />
|
||||||
<span><br />Your code signing token is no longer valid.<br/><br/>
|
<span
|
||||||
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/>
|
><br />Your code signing token is no longer valid.<br /><br />
|
||||||
For any issues or to renew your sponsorship please email support@amidaware.com<br/><br/></span>
|
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
|
<q-btn
|
||||||
color="dark"
|
color="dark"
|
||||||
icon="refresh"
|
icon="refresh"
|
||||||
@@ -51,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"
|
||||||
|
>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"
|
||||||
@@ -89,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>
|
||||||
@@ -108,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>
|
||||||
@@ -135,6 +160,32 @@
|
|||||||
<q-item-label>Preferences</q-item-label>
|
<q-item-label>Preferences</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</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 to="/expired" exact>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label>Logout</q-item-label>
|
<q-item-label>Logout</q-item-label>
|
||||||
@@ -156,10 +207,13 @@ import { useQuasar } from "quasar";
|
|||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { getWSUrl } from "@/websocket/channels";
|
import { getWSUrl } from "@/websocket/channels";
|
||||||
|
import { resetTwoFactor } from "@/api/accounts";
|
||||||
|
import { notifySuccess } from "@/utils/notify";
|
||||||
|
|
||||||
// ui imports
|
// ui imports
|
||||||
import AlertsIcon from "@/components/AlertsIcon.vue";
|
import AlertsIcon from "@/components/AlertsIcon.vue";
|
||||||
import UserPreferences from "@/components/modals/coresettings/UserPreferences.vue";
|
import UserPreferences from "@/components/modals/coresettings/UserPreferences.vue";
|
||||||
|
import ResetPass from "@/components/accounts/ResetPass.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MainLayout",
|
name: "MainLayout",
|
||||||
@@ -184,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
|
||||||
@@ -197,10 +253,31 @@ export default {
|
|||||||
}).onOk(() => store.dispatch("getDashInfo"));
|
}).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 serverCount = ref(0);
|
||||||
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);
|
||||||
|
|
||||||
@@ -208,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);
|
||||||
@@ -220,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 {
|
||||||
@@ -243,13 +328,18 @@ export default {
|
|||||||
poll.value = setInterval(() => {
|
poll.value = setInterval(() => {
|
||||||
store.dispatch("checkVer");
|
store.dispatch("checkVer");
|
||||||
store.dispatch("getDashInfo", false);
|
store.dispatch("getDashInfo", false);
|
||||||
}, 60 * 5 * 1000);
|
}, 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();
|
||||||
@@ -270,6 +360,7 @@ export default {
|
|||||||
serverOfflineCount,
|
serverOfflineCount,
|
||||||
workstationCount,
|
workstationCount,
|
||||||
workstationOfflineCount,
|
workstationOfflineCount,
|
||||||
|
daysUntilCertExpires,
|
||||||
latestReleaseURL,
|
latestReleaseURL,
|
||||||
currentTRMMVersion,
|
currentTRMMVersion,
|
||||||
latestTRMMVersion,
|
latestTRMMVersion,
|
||||||
@@ -278,9 +369,13 @@ export default {
|
|||||||
darkMode,
|
darkMode,
|
||||||
hosted,
|
hosted,
|
||||||
tokenExpired,
|
tokenExpired,
|
||||||
|
dash_warning_color,
|
||||||
|
dash_negative_color,
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
showUserPreferences,
|
showUserPreferences,
|
||||||
|
resetPassword,
|
||||||
|
reset2FA,
|
||||||
updateAvailable,
|
updateAvailable,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ export default {
|
|||||||
value: script.id,
|
value: script.id,
|
||||||
timeout: script.default_timeout,
|
timeout: script.default_timeout,
|
||||||
args: script.args,
|
args: script.args,
|
||||||
|
env_vars: script.env_vars,
|
||||||
});
|
});
|
||||||
} else if (cat === "Unassigned" && !script.category) {
|
} else if (cat === "Unassigned" && !script.category) {
|
||||||
tmp.push({
|
tmp.push({
|
||||||
@@ -200,6 +201,7 @@ export default {
|
|||||||
value: script.id,
|
value: script.id,
|
||||||
timeout: script.default_timeout,
|
timeout: script.default_timeout,
|
||||||
args: script.args,
|
args: script.args,
|
||||||
|
env_vars: script.env_vars,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ export default function () {
|
|||||||
currentTRMMVersion: null,
|
currentTRMMVersion: null,
|
||||||
latestTRMMVersion: null,
|
latestTRMMVersion: null,
|
||||||
dateFormat: "MMM-DD-YYYY - HH:mm",
|
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: {
|
getters: {
|
||||||
@@ -136,6 +146,24 @@ export default function () {
|
|||||||
setDateFormat(state, val) {
|
setDateFormat(state, val) {
|
||||||
state.dateFormat = 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: {
|
actions: {
|
||||||
setClientTreeSplitter(context, val) {
|
setClientTreeSplitter(context, val) {
|
||||||
@@ -160,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);
|
||||||
@@ -194,107 +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);
|
||||||
|
commit("setOpenAIIntegrationStatus", data.open_ai_integration_enabled);
|
||||||
|
commit("setRunCmdPlaceholders", data.run_cmd_placeholder_text);
|
||||||
|
|
||||||
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) => {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export function formatScriptOptions(data) {
|
|||||||
value: script.id,
|
value: script.id,
|
||||||
timeout: script.default_timeout,
|
timeout: script.default_timeout,
|
||||||
args: script.args,
|
args: script.args,
|
||||||
|
env_vars: script.env_vars,
|
||||||
filename: script.filename,
|
filename: script.filename,
|
||||||
syntax: script.syntax,
|
syntax: script.syntax,
|
||||||
script_type: script.script_type,
|
script_type: script.script_type,
|
||||||
@@ -80,6 +81,7 @@ export function formatScriptOptions(data) {
|
|||||||
value: script.id,
|
value: script.id,
|
||||||
timeout: script.default_timeout,
|
timeout: script.default_timeout,
|
||||||
args: script.args,
|
args: script.args,
|
||||||
|
env_vars: script.env_vars,
|
||||||
filename: script.filename,
|
filename: script.filename,
|
||||||
syntax: script.syntax,
|
syntax: script.syntax,
|
||||||
script_type: script.script_type,
|
script_type: script.script_type,
|
||||||
|
|||||||
@@ -173,6 +173,18 @@
|
|||||||
</q-menu>
|
</q-menu>
|
||||||
</q-item>
|
</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-separator></q-separator>
|
||||||
|
|
||||||
<q-item clickable v-close-popup>
|
<q-item clickable v-close-popup>
|
||||||
@@ -440,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: "",
|
search: this.$route.query.search ? this.$route.query.search : "",
|
||||||
filterTextLength: 0,
|
filterTextLength: 0,
|
||||||
filterAvailability: "all",
|
filterAvailability: "all",
|
||||||
filterPatchesPending: false,
|
filterPatchesPending: false,
|
||||||
@@ -690,6 +702,17 @@ export default {
|
|||||||
})
|
})
|
||||||
.onOk(() => this.$store.dispatch("refreshDashboard"));
|
.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) {
|
showToggleMaintenance(node) {
|
||||||
let data = {
|
let data = {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user