Compare commits
52 Commits
v0.101.21-
...
v0.101.30
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b2286c4f8 | ||
|
|
34233fde2f | ||
|
|
e95dd5f6e7 | ||
|
|
901e6986a0 | ||
|
|
aa78929743 | ||
|
|
8207f30234 | ||
|
|
1879977b83 | ||
|
|
b4de579a74 | ||
|
|
23f15ff9e5 | ||
|
|
498e038bbb | ||
|
|
bb1f1c19cf | ||
|
|
74e3aa4e46 | ||
|
|
07a8e3ebcb | ||
|
|
89966dd006 | ||
|
|
68036f6837 | ||
|
|
45ac82b1dd | ||
|
|
d94e5c7965 | ||
|
|
e0c1b3199a | ||
|
|
fdbbdf7394 | ||
|
|
03fae45ac5 | ||
|
|
346670e8ea | ||
|
|
e030efaecf | ||
|
|
b8a4f9fe74 | ||
|
|
f963b51d70 | ||
|
|
feacb19cf9 | ||
|
|
7ce2c1e969 | ||
|
|
d1defcef4a | ||
|
|
e674b4fa5d | ||
|
|
b08a5a6c2d | ||
|
|
9fa1d7209f | ||
|
|
2adfccfa1d | ||
|
|
04766efcd0 | ||
|
|
4babb937f6 | ||
|
|
c2591c9e7d | ||
|
|
69403def2a | ||
|
|
7fcbe6fbd8 | ||
|
|
a2f472ef9c | ||
|
|
8403ac0e93 | ||
|
|
b7a91563b0 | ||
|
|
ab19afca16 | ||
|
|
f24c6a7a80 | ||
|
|
99490bf859 | ||
|
|
72cdeeaa6a | ||
|
|
1eca4d605b | ||
|
|
52ee98f6f8 | ||
|
|
d270b877c9 | ||
|
|
fd8b2a1d98 | ||
|
|
f518043d8d | ||
|
|
cc2335558d | ||
|
|
a8a171ba2c | ||
|
|
24a63f477e | ||
|
|
ddeb6293a1 |
3
.github/workflows/build-release.yml
vendored
3
.github/workflows/build-release.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- run: touch env-config.js
|
||||
|
||||
@@ -32,4 +32,3 @@ jobs:
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: trmm-web-${{github.ref_name}}.tar.gz
|
||||
|
||||
|
||||
2
.github/workflows/frontend-linting.yml
vendored
2
.github/workflows/frontend-linting.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- run: npm install
|
||||
|
||||
- name: Run Prettier formatting
|
||||
|
||||
1921
package-lock.json
generated
1921
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
46
package.json
46
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.101.21-dev",
|
||||
"version": "0.101.30",
|
||||
"private": true,
|
||||
"productName": "Tactical RMM",
|
||||
"scripts": {
|
||||
@@ -10,31 +10,31 @@
|
||||
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "1.16.4",
|
||||
"apexcharts": "3.40.0",
|
||||
"axios": "1.4.0",
|
||||
"dotenv": "16.0.3",
|
||||
"qrcode.vue": "3.4.0",
|
||||
"quasar": "2.12.0",
|
||||
"vue": "3.2.47",
|
||||
"vue3-ace-editor": "2.2.2",
|
||||
"vue3-apexcharts": "1.4.1",
|
||||
"@quasar/extras": "1.16.7",
|
||||
"apexcharts": "3.41.1",
|
||||
"axios": "1.5.1",
|
||||
"dotenv": "16.3.1",
|
||||
"qrcode.vue": "3.4.1",
|
||||
"quasar": "2.12.7",
|
||||
"vue": "3.3.4",
|
||||
"vue3-ace-editor": "2.2.3",
|
||||
"vue3-apexcharts": "1.4.4",
|
||||
"vuedraggable": "4.1.0",
|
||||
"vue-router": "4.1.6",
|
||||
"vue-router": "4.2.5",
|
||||
"vuex": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/cli": "^2.2.1",
|
||||
"@intlify/unplugin-vue-i18n": "^0.10.0",
|
||||
"@quasar/app-vite": "^1.4.3",
|
||||
"@types/node": "^20.2.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||
"@typescript-eslint/parser": "^5.59.7",
|
||||
"autoprefixer": "10.4.14",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
"@quasar/cli": "2.3.0",
|
||||
"@intlify/unplugin-vue-i18n": "1.4.0",
|
||||
"@quasar/app-vite": "1.6.2",
|
||||
"@types/node": "20.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.3",
|
||||
"@typescript-eslint/parser": "6.7.3",
|
||||
"autoprefixer": "10.4.16",
|
||||
"eslint": "8.50.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-vue": "8.7.1",
|
||||
"prettier": "2.8.8",
|
||||
"typescript": "5.0.4"
|
||||
"prettier": "3.0.3",
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ export async function resetCheck(id) {
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function resetAllChecksStatus(agent_id) {
|
||||
const { data } = await axios.post(`${baseUrl}/${agent_id}/resetall/`);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function runAgentChecks(agent_id) {
|
||||
const { data } = await axios.post(`${baseUrl}/${agent_id}/run/`);
|
||||
return data;
|
||||
|
||||
@@ -119,6 +119,16 @@
|
||||
no-caps
|
||||
icon="play_arrow"
|
||||
@click="runChecks"
|
||||
class="q-mr-md"
|
||||
/>
|
||||
<q-btn
|
||||
label="Reset All Checks Status"
|
||||
dense
|
||||
flat
|
||||
push
|
||||
no-caps
|
||||
icon="restart_alt"
|
||||
@click="resetAllChecks"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -415,6 +425,7 @@ import {
|
||||
updateCheck,
|
||||
removeCheck,
|
||||
resetCheck,
|
||||
resetAllChecksStatus,
|
||||
runAgentChecks,
|
||||
} from "@/api/checks";
|
||||
import { fetchAgentChecks } from "@/api/agents";
|
||||
@@ -572,7 +583,7 @@ export default {
|
||||
notifySuccess(result);
|
||||
refreshDashboard(
|
||||
false /* clearTreeSelected */,
|
||||
false /* clearSubTable */
|
||||
false /* clearSubTable */,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -580,6 +591,27 @@ export default {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
function resetAllChecks() {
|
||||
console.info(selectedAgent.value);
|
||||
$q.dialog({
|
||||
title: "Are you sure?",
|
||||
message: "Reset all checks status",
|
||||
cancel: true,
|
||||
ok: { label: "Reset", color: "negative" },
|
||||
persistent: true,
|
||||
}).onOk(async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await resetAllChecksStatus(selectedAgent.value);
|
||||
await getChecks();
|
||||
notifySuccess(result);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function showEventInfo(data) {
|
||||
$q.dialog({
|
||||
component: EventLogCheckOutput,
|
||||
@@ -674,6 +706,7 @@ export default {
|
||||
formatDate,
|
||||
getAlertSeverity,
|
||||
runChecks,
|
||||
resetAllChecks,
|
||||
|
||||
// dialogs
|
||||
showScriptOutput,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
ref="dialogRef"
|
||||
@hide="onDialogHide"
|
||||
persistent
|
||||
@keydown.esc="onDialogHide"
|
||||
@keydown.esc.stop="onDialogHide"
|
||||
:maximized="maximized"
|
||||
>
|
||||
<q-card
|
||||
|
||||
@@ -286,15 +286,10 @@
|
||||
</template>
|
||||
</q-tree>
|
||||
</div>
|
||||
<q-table
|
||||
<tactical-table
|
||||
v-if="tableView"
|
||||
dense
|
||||
:table-class="{
|
||||
'table-bgcolor': !$q.dark.isActive,
|
||||
'table-bgcolor-dark': $q.dark.isActive,
|
||||
}"
|
||||
:style="{ 'max-height': `${$q.screen.height - 182}px` }"
|
||||
class="tbl-sticky"
|
||||
:rows="visibleScripts"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
@@ -304,6 +299,7 @@
|
||||
binary-state-sort
|
||||
virtual-scroll
|
||||
:rows-per-page-options="[0]"
|
||||
column-select
|
||||
>
|
||||
<template v-slot:header-cell-favorite="props">
|
||||
<q-th :props="props" auto-width>
|
||||
@@ -425,7 +421,7 @@
|
||||
</q-list>
|
||||
</q-menu>
|
||||
<!-- favorite -->
|
||||
<q-td>
|
||||
<q-td key="favorite" :props="props">
|
||||
<q-icon
|
||||
v-if="props.row.favorite"
|
||||
color="yellow-8"
|
||||
@@ -434,7 +430,7 @@
|
||||
/>
|
||||
</q-td>
|
||||
<!-- shell icon -->
|
||||
<q-td>
|
||||
<q-td key="shell" :props="props">
|
||||
<q-icon
|
||||
v-if="props.row.shell === 'powershell'"
|
||||
name="mdi-powershell"
|
||||
@@ -469,7 +465,7 @@
|
||||
</q-icon>
|
||||
</q-td>
|
||||
<!-- supported platforms -->
|
||||
<q-td>
|
||||
<q-td key="supported_platforms" :props="props">
|
||||
<q-badge
|
||||
v-if="
|
||||
!props.row.supported_platforms ||
|
||||
@@ -487,7 +483,11 @@
|
||||
>
|
||||
</q-td>
|
||||
<!-- name -->
|
||||
<q-td :style="{ color: props.row.hidden ? 'grey' : '' }">
|
||||
<q-td
|
||||
key="name"
|
||||
:props="props"
|
||||
:style="{ color: props.row.hidden ? 'grey' : '' }"
|
||||
>
|
||||
{{ truncateText(props.row.name, 50) }}
|
||||
<q-tooltip
|
||||
v-if="props.row.name.length >= 50"
|
||||
@@ -497,7 +497,7 @@
|
||||
</q-tooltip>
|
||||
</q-td>
|
||||
<!-- args -->
|
||||
<q-td>
|
||||
<q-td key="args" :props="props">
|
||||
<span v-if="props.row.args.length > 0">
|
||||
{{ truncateText(props.row.args.toString(), 30) }}
|
||||
<q-tooltip
|
||||
@@ -509,8 +509,8 @@
|
||||
</span>
|
||||
</q-td>
|
||||
|
||||
<q-td>{{ props.row.category }}</q-td>
|
||||
<q-td>
|
||||
<q-td key="category" :props="props">{{ props.row.category }}</q-td>
|
||||
<q-td key="desc" :props="props">
|
||||
{{ truncateText(props.row.description, 30) }}
|
||||
<q-tooltip
|
||||
v-if="props.row.description.length >= 30"
|
||||
@@ -518,10 +518,13 @@
|
||||
>{{ props.row.description }}</q-tooltip
|
||||
>
|
||||
</q-td>
|
||||
<q-td>{{ props.row.default_timeout }}</q-td>
|
||||
<q-td key="default_timeout" :props="props">{{
|
||||
props.row.default_timeout
|
||||
}}</q-td>
|
||||
<q-td></q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</tactical-table>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
@@ -545,12 +548,13 @@ import { notifySuccess } from "@/utils/notify";
|
||||
import ScriptUploadModal from "@/components/scripts/ScriptUploadModal.vue";
|
||||
import ScriptFormModal from "@/components/scripts/ScriptFormModal.vue";
|
||||
import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue";
|
||||
import TacticalTable from "@/components/ui/TacticalTable.vue";
|
||||
|
||||
// static data
|
||||
const columns = [
|
||||
{
|
||||
name: "favorite",
|
||||
label: "",
|
||||
label: "Favorites",
|
||||
field: "favorite",
|
||||
align: "left",
|
||||
sortable: true,
|
||||
@@ -608,6 +612,9 @@ const columns = [
|
||||
|
||||
export default {
|
||||
name: "ScriptManager",
|
||||
components: {
|
||||
TacticalTable,
|
||||
},
|
||||
emits: [...useDialogPluginComponent.emits],
|
||||
setup() {
|
||||
// setup vuex store
|
||||
|
||||
107
src/components/ui/TacticalTable.vue
Normal file
107
src/components/ui/TacticalTable.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<q-table
|
||||
:columns="localColumns"
|
||||
:visible-columns="visibleColumns"
|
||||
:table-class="{
|
||||
'table-bgcolor': !$q.dark.isActive,
|
||||
'table-bgcolor-dark': $q.dark.isActive,
|
||||
'column-bgcolor-dark': $q.dark.isActive && columnSelect,
|
||||
'column-bgcolor': !$q.dark.isActive && columnSelect,
|
||||
'sticky-header-right-column': columnSelect,
|
||||
'tbl-sticky': !columnSelect,
|
||||
}"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template v-for="(_, slot) in $slots" v-slot:[slot]="scope">
|
||||
<slot :name="slot" v-bind="scope || {}" />
|
||||
</template>
|
||||
|
||||
<template v-slot:header-cell-columnSelect="props">
|
||||
<q-th :props="props" auto-width>
|
||||
<q-btn dense flat icon="more_horiz">
|
||||
<q-menu>
|
||||
<q-option-group
|
||||
v-model="visibleColumns"
|
||||
:options="columnOptions"
|
||||
type="checkbox"
|
||||
/>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</q-th>
|
||||
</template>
|
||||
</q-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { type QTableColumn } from "quasar";
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
columns: QTableColumn[];
|
||||
columnSelect?: boolean;
|
||||
excludeColumns?: string[];
|
||||
}>(),
|
||||
{ columnSelect: false, excludeColumns: () => ["columnSelect"] }
|
||||
);
|
||||
// save a non-reactive copy of columns to modify
|
||||
const localColumns: QTableColumn[] = Object.assign([], props.columns);
|
||||
if (props.columnSelect)
|
||||
localColumns.push({
|
||||
name: "columnSelect",
|
||||
label: "Column Select",
|
||||
field: "columnSelect",
|
||||
});
|
||||
const visibleColumns = ref(localColumns.map((column) => column.name));
|
||||
const columnOptions = ref(
|
||||
localColumns
|
||||
.filter((column) => !props.excludeColumns.includes(column.name))
|
||||
.map((column) => ({ label: column.label, value: column.name }))
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
||||
.column-bgcolor-dark
|
||||
td:last-child
|
||||
/* bg color is important for td; just specify one */
|
||||
background-color: #1d1d1d
|
||||
|
||||
.column-bgcolor
|
||||
td:last-child
|
||||
/* bg color is important for td; just specify one */
|
||||
background-color: #ffffff
|
||||
|
||||
.sticky-header-right-column
|
||||
tr th
|
||||
position: sticky
|
||||
/* higher than z-index for td below */
|
||||
z-index: 2
|
||||
/* this will be the loading indicator */
|
||||
thead tr:last-child th
|
||||
/* height of all previous header rows */
|
||||
top: 48px
|
||||
/* highest z-index */
|
||||
z-index: 3
|
||||
thead tr:last-child th
|
||||
top: 0
|
||||
z-index: 1
|
||||
tr:last-child th:last-child
|
||||
/* highest z-index */
|
||||
z-index: 3
|
||||
td:last-child
|
||||
z-index: 1
|
||||
td:last-child, th:last-child
|
||||
position: sticky
|
||||
right: 0
|
||||
/* prevent scrolling behind sticky top row on focus */
|
||||
tbody
|
||||
/* height of all previous header rows */
|
||||
scroll-margin-top: 48px
|
||||
</style>
|
||||
@@ -74,7 +74,7 @@
|
||||
:color="dash_negative_color"
|
||||
text-color="black"
|
||||
icon="warning"
|
||||
>Certificate expires in {{ daysUntilCertExpires }} days</q-chip
|
||||
>SSL certificate expires in {{ daysUntilCertExpires }} days</q-chip
|
||||
>
|
||||
</q-toolbar-title>
|
||||
<!-- temp dark mode toggle -->
|
||||
@@ -288,7 +288,7 @@ export default {
|
||||
|
||||
if (!token.value) {
|
||||
console.log(
|
||||
"Access token is null or invalid, not setting up WebSocket"
|
||||
"Access token is null or invalid, not setting up WebSocket",
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -325,10 +325,13 @@ export default {
|
||||
|
||||
const poll = ref(null);
|
||||
function livePoll() {
|
||||
poll.value = setInterval(() => {
|
||||
store.dispatch("checkVer");
|
||||
store.dispatch("getDashInfo", false);
|
||||
}, 60 * 4 * 1000);
|
||||
poll.value = setInterval(
|
||||
() => {
|
||||
store.dispatch("checkVer");
|
||||
store.dispatch("getDashInfo", false);
|
||||
},
|
||||
60 * 4 * 1000,
|
||||
);
|
||||
}
|
||||
|
||||
const updateAvailable = computed(() => {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
>
|
||||
<q-spinner size="40px" color="primary" />
|
||||
</div>
|
||||
<div v-else class="q-pa-sm q-gutter-sm scroll" style="height: 85vh">
|
||||
<div v-else class="q-pa-sm q-gutter-sm scroll" style="height: 85vh; overflow: initial;">
|
||||
<q-list dense class="rounded-borders">
|
||||
<q-item
|
||||
clickable
|
||||
|
||||
@@ -4,8 +4,17 @@
|
||||
<div class="col"></div>
|
||||
<div class="col">
|
||||
<q-card>
|
||||
<q-card-actions align="center">
|
||||
<q-btn
|
||||
label="Getting Started"
|
||||
color="info"
|
||||
class="full-width"
|
||||
href="https://docs.tacticalrmm.com/guide_gettingstarted/"
|
||||
target="_blank"
|
||||
/>
|
||||
</q-card-actions>
|
||||
<q-card-section class="row items-center">
|
||||
<div class="text-h6">Initial Setup</div>
|
||||
<div class="text-h5 text-weight-bold">Initial Setup</div>
|
||||
</q-card-section>
|
||||
<q-form @submit.prevent="finish">
|
||||
<q-card-section>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<q-bar>
|
||||
<span class="text-caption">
|
||||
Agent Status:
|
||||
TRMM Agent Status:
|
||||
<q-badge :color="statusColor" :label="status" />
|
||||
</span>
|
||||
<q-space />
|
||||
|
||||
Reference in New Issue
Block a user