mirror of
https://github.com/jpros/tacticalrmm-web.git
synced 2026-02-21 11:51:08 +00:00
895 lines
29 KiB
Vue
895 lines
29 KiB
Vue
<template>
|
|
<q-layout view="hHh lpR fFf">
|
|
<q-header elevated class="bg-grey-9 text-white">
|
|
<q-banner v-if="needRefresh" inline-actions class="bg-red text-white text-center">
|
|
You are viewing an outdated version of this page.
|
|
<q-btn color="dark" icon="refresh" label="Refresh" @click="reload" />
|
|
</q-banner>
|
|
<q-toolbar>
|
|
<q-btn dense flat push @click="refreshEntireSite" icon="refresh" />
|
|
<q-toolbar-title>
|
|
Tactical RMM<span class="text-overline q-ml-sm">v{{ currentTRMMVersion }}</span>
|
|
<span
|
|
class="text-overline q-ml-md"
|
|
v-if="latestTRMMVersion !== 'error' && currentTRMMVersion !== latestTRMMVersion"
|
|
><q-badge color="warning"
|
|
><a :href="latestReleaseURL" target="_blank">v{{ latestTRMMVersion }} available</a></q-badge
|
|
></span
|
|
>
|
|
</q-toolbar-title>
|
|
|
|
<!-- temp dark mode toggle -->
|
|
<q-toggle
|
|
v-model="darkMode"
|
|
class="q-mr-sm"
|
|
@input="toggleDark(darkMode)"
|
|
checked-icon="nights_stay"
|
|
unchecked-icon="wb_sunny"
|
|
/>
|
|
|
|
<!-- Devices Chip -->
|
|
<q-chip class="cursor-pointer">
|
|
<q-avatar size="md" icon="devices" color="primary" />
|
|
<q-tooltip :delay="600" anchor="top middle" self="top middle">Agent Count</q-tooltip>
|
|
{{ totalAgents }}
|
|
<q-menu>
|
|
<q-list dense>
|
|
<q-item-label header>Servers</q-item-label>
|
|
<q-item>
|
|
<q-item-section avatar>
|
|
<q-icon name="fa fa-server" size="sm" color="primary" />
|
|
</q-item-section>
|
|
|
|
<q-item-section no-wrap>
|
|
<q-item-label>Total: {{ serverCount }}</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
<q-item>
|
|
<q-item-section avatar>
|
|
<q-icon name="power_off" size="sm" color="negative" />
|
|
</q-item-section>
|
|
|
|
<q-item-section no-wrap>
|
|
<q-item-label>Offline: {{ serverOfflineCount }}</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
<q-item-label header>Workstations</q-item-label>
|
|
<q-item>
|
|
<q-item-section avatar>
|
|
<q-icon name="computer" size="sm" color="primary" />
|
|
</q-item-section>
|
|
|
|
<q-item-section no-wrap>
|
|
<q-item-label>Total: {{ workstationCount }}</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
<q-item>
|
|
<q-item-section avatar>
|
|
<q-icon name="power_off" size="sm" color="negative" />
|
|
</q-item-section>
|
|
|
|
<q-item-section no-wrap>
|
|
<q-item-label>Offline: {{ workstationOfflineCount }}</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-chip>
|
|
|
|
<AlertsIcon />
|
|
|
|
<q-btn-dropdown flat no-caps stretch :label="user">
|
|
<q-list>
|
|
<q-item clickable v-ripple @click="showUserPreferencesModal = true" v-close-popup>
|
|
<q-item-section>
|
|
<q-item-label>Preferences</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
<q-item to="/expired" exact>
|
|
<q-item-section>
|
|
<q-item-label>Logout</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-btn-dropdown>
|
|
</q-toolbar>
|
|
</q-header>
|
|
|
|
<q-page-container>
|
|
<FileBar />
|
|
<q-splitter v-model="clientTreeSplitter">
|
|
<template v-slot:before>
|
|
<div v-if="!treeReady" class="q-pa-sm q-gutter-sm text-center" style="height: 30vh">
|
|
<q-spinner size="40px" color="primary" />
|
|
</div>
|
|
<div v-else class="q-pa-sm q-gutter-sm scroll" style="height: 85vh">
|
|
<q-list dense class="rounded-borders">
|
|
<q-item clickable v-ripple :active="allClientsActive" @click="clearTreeSelected">
|
|
<q-item-section avatar>
|
|
<q-icon name="fas fa-home" />
|
|
</q-item-section>
|
|
<q-item-section>All Clients</q-item-section>
|
|
</q-item>
|
|
<q-tree
|
|
ref="tree"
|
|
:nodes="clientsTree"
|
|
node-key="raw"
|
|
no-nodes-label="No Clients"
|
|
selected-color="primary"
|
|
:selected.sync="selectedTree"
|
|
@update:selected="loadFrame(selectedTree)"
|
|
>
|
|
<template v-slot:default-header="props">
|
|
<div class="row">
|
|
<q-icon :name="props.node.icon" :color="props.node.color" class="q-mr-sm" />
|
|
<span>{{ props.node.label }}</span>
|
|
|
|
<q-menu context-menu>
|
|
<q-list dense style="min-width: 200px">
|
|
<q-item clickable v-close-popup @click="showEditModal(props.node)">
|
|
<q-item-section side>
|
|
<q-icon name="edit" />
|
|
</q-item-section>
|
|
<q-item-section>Edit</q-item-section>
|
|
</q-item>
|
|
<q-item clickable v-close-popup @click="showDeleteModal(props.node)">
|
|
<q-item-section side>
|
|
<q-icon name="delete" />
|
|
</q-item-section>
|
|
<q-item-section>Delete</q-item-section>
|
|
</q-item>
|
|
|
|
<q-separator></q-separator>
|
|
|
|
<q-item
|
|
v-if="props.node.children"
|
|
clickable
|
|
v-close-popup
|
|
@click="showAddSiteModal(props.node)"
|
|
>
|
|
<q-item-section side>
|
|
<q-icon name="add" />
|
|
</q-item-section>
|
|
<q-item-section>Add Site</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item clickable v-close-popup @click="showToggleMaintenance(props.node)">
|
|
<q-item-section side>
|
|
<q-icon name="construction" />
|
|
</q-item-section>
|
|
<q-item-section>{{ menuMaintenanceText(props.node) }}</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item
|
|
v-if="props.node.children === undefined"
|
|
clickable
|
|
v-close-popup
|
|
@click="showInstallAgent(props.node)"
|
|
>
|
|
<q-item-section side>
|
|
<q-icon name="cloud_download" />
|
|
</q-item-section>
|
|
<q-item-section>Install Agent</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item clickable v-close-popup @click="showPolicyAdd(props.node)">
|
|
<q-item-section side>
|
|
<q-icon name="policy" />
|
|
</q-item-section>
|
|
<q-item-section>Assign Automation Policy</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item clickable v-close-popup @click="showAlertTemplateAdd(props.node)">
|
|
<q-item-section side>
|
|
<q-icon name="error" />
|
|
</q-item-section>
|
|
<q-item-section>Assign Alert Template</q-item-section>
|
|
</q-item>
|
|
|
|
<q-separator></q-separator>
|
|
|
|
<q-item clickable v-close-popup>
|
|
<q-item-section>Close</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</div>
|
|
</template>
|
|
</q-tree>
|
|
</q-list>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-slot:after>
|
|
<q-splitter v-model="innerModel" reverse horizontal style="height: 87vh" @input="setSplitter(innerModel)">
|
|
<template v-slot:before>
|
|
<div class="row">
|
|
<q-tabs
|
|
v-model="tab"
|
|
dense
|
|
no-caps
|
|
inline-label
|
|
class="text-grey"
|
|
active-color="primary"
|
|
indicator-color="primary"
|
|
align="left"
|
|
narrow-indicator
|
|
>
|
|
<q-tab name="server" icon="fas fa-server" label="Servers" />
|
|
<q-tab name="workstation" icon="computer" label="Workstations" />
|
|
<q-tab name="mixed" label="Mixed" />
|
|
</q-tabs>
|
|
<q-space />
|
|
<q-input
|
|
v-model="search"
|
|
style="width: 450px"
|
|
label="Search"
|
|
dense
|
|
outlined
|
|
clearable
|
|
@clear="clearFilter"
|
|
class="q-pr-md q-pb-xs"
|
|
>
|
|
<template v-slot:prepend>
|
|
<q-icon name="search" color="primary" />
|
|
</template>
|
|
<template v-slot:after>
|
|
<q-btn round dense flat icon="filter_alt" :color="isFilteringTable ? 'green' : ''">
|
|
<q-menu>
|
|
<q-list dense>
|
|
<q-item-label header>Filter Agent Table</q-item-label>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-checkbox v-model="filterChecksFailing" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Checks Failing</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-checkbox v-model="filterPatchesPending" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Patches Pending</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-checkbox v-model="filterActionsPending" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Actions Pending</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-checkbox v-model="filterRebootNeeded" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Reboot Needed</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item-label header>Availability</q-item-label>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-radio val="all" v-model="filterAvailability" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Show All Agents</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-radio val="online" v-model="filterAvailability" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Show Online Only</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-radio val="offline" v-model="filterAvailability" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Show Offline Only</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<q-item>
|
|
<q-item-section side>
|
|
<q-radio val="offline_30days" v-model="filterAvailability" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Show Offline for over 30 days</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
|
|
<div class="row no-wrap q-pa-md">
|
|
<div class="column">
|
|
<q-btn v-close-popup label="Apply" color="primary" @click="applyFilter" />
|
|
</div>
|
|
<q-space />
|
|
<div class="column">
|
|
<q-btn label="Clear" @click="clearFilter" />
|
|
</div>
|
|
</div>
|
|
</q-menu>
|
|
</q-btn>
|
|
</template>
|
|
</q-input>
|
|
</div>
|
|
<AgentTable
|
|
:frame="filteredAgents"
|
|
:columns="columns"
|
|
:tab="tab"
|
|
:userName="user"
|
|
:search="search"
|
|
:visibleColumns="visibleColumns"
|
|
@refreshEdit="refreshEntireSite"
|
|
/>
|
|
</template>
|
|
<template v-slot:separator>
|
|
<q-avatar color="primary" text-color="white" size="30px" icon="drag_indicator" />
|
|
</template>
|
|
<template v-slot:after>
|
|
<SubTableTabs @refreshEdit="refreshEntireSite" />
|
|
</template>
|
|
</q-splitter>
|
|
</template>
|
|
</q-splitter>
|
|
</q-page-container>
|
|
|
|
<!-- install agent modal -->
|
|
<q-dialog v-model="showInstallAgentModal" @hide="closeInstallAgent">
|
|
<InstallAgent @close="closeInstallAgent" :sitepk="parseInt(sitePk)" />
|
|
</q-dialog>
|
|
<!-- user preferences modal -->
|
|
<q-dialog v-model="showUserPreferencesModal">
|
|
<UserPreferences @close="showUserPreferencesModal = false" @edited="getDashInfo" />
|
|
</q-dialog>
|
|
</q-layout>
|
|
</template>
|
|
|
|
<script>
|
|
import mixins from "@/mixins/mixins";
|
|
import { mapState, mapGetters } from "vuex";
|
|
import { getBaseUrl } from "@/boot/axios";
|
|
import FileBar from "@/components/FileBar";
|
|
import AgentTable from "@/components/AgentTable";
|
|
import SubTableTabs from "@/components/SubTableTabs";
|
|
import AlertsIcon from "@/components/AlertsIcon";
|
|
import PolicyAdd from "@/components/automation/modals/PolicyAdd";
|
|
import ClientsForm from "@/components/modals/clients/ClientsForm";
|
|
import SitesForm from "@/components/modals/clients/SitesForm";
|
|
import DeleteClient from "@/components/modals/clients/DeleteClient";
|
|
import InstallAgent from "@/components/modals/agents/InstallAgent";
|
|
import UserPreferences from "@/components/modals/coresettings/UserPreferences";
|
|
import AlertTemplateAdd from "@/components/modals/alerts/AlertTemplateAdd";
|
|
|
|
export default {
|
|
components: {
|
|
FileBar,
|
|
AgentTable,
|
|
SubTableTabs,
|
|
AlertsIcon,
|
|
InstallAgent,
|
|
UserPreferences,
|
|
},
|
|
mixins: [mixins],
|
|
data() {
|
|
return {
|
|
ws: null,
|
|
darkMode: true,
|
|
showInstallAgentModal: false,
|
|
sitePk: null,
|
|
serverCount: 0,
|
|
serverOfflineCount: 0,
|
|
workstationCount: 0,
|
|
workstationOfflineCount: 0,
|
|
selectedTree: "",
|
|
innerModel: 50,
|
|
clientActive: "",
|
|
siteActive: "",
|
|
frame: [],
|
|
poll: null,
|
|
search: "",
|
|
filterTextLength: 0,
|
|
filterAvailability: "all",
|
|
filterPatchesPending: false,
|
|
filterActionsPending: false,
|
|
filterChecksFailing: false,
|
|
filterRebootNeeded: false,
|
|
currentTRMMVersion: null,
|
|
latestTRMMVersion: "error",
|
|
showUserPreferencesModal: false,
|
|
columns: [
|
|
{
|
|
name: "smsalert",
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "emailalert",
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "dashboardalert",
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "checks-status",
|
|
align: "left",
|
|
field: "checks",
|
|
sortable: true,
|
|
sort: (a, b, rowA, rowB) => parseInt(b.failing) - a.failing,
|
|
},
|
|
{
|
|
name: "client_name",
|
|
label: "Client",
|
|
field: "client_name",
|
|
sortable: true,
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "site_name",
|
|
label: "Site",
|
|
field: "site_name",
|
|
sortable: true,
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "hostname",
|
|
label: "Hostname",
|
|
field: "hostname",
|
|
sortable: true,
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "description",
|
|
label: "Description",
|
|
field: "description",
|
|
sortable: true,
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "user",
|
|
label: "User",
|
|
field: "logged_username",
|
|
sortable: true,
|
|
align: "left",
|
|
},
|
|
{
|
|
name: "italic",
|
|
field: "italic",
|
|
},
|
|
{
|
|
name: "patchespending",
|
|
field: "patches_pending",
|
|
align: "left",
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "pendingactions",
|
|
field: "pending_actions",
|
|
align: "left",
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "needs_reboot",
|
|
field: "needs_reboot",
|
|
align: "left",
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "agentstatus",
|
|
field: "status",
|
|
align: "left",
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "last_seen",
|
|
label: "Last Response",
|
|
field: "last_seen",
|
|
sortable: true,
|
|
align: "left",
|
|
sort: (a, b) => this.dateStringToUnix(a) - this.dateStringToUnix(b),
|
|
},
|
|
{
|
|
name: "boot_time",
|
|
label: "Boot Time",
|
|
field: "boot_time",
|
|
sortable: true,
|
|
align: "left",
|
|
},
|
|
],
|
|
visibleColumns: [
|
|
"smsalert",
|
|
"emailalert",
|
|
"dashboardalert",
|
|
"checks-status",
|
|
"client_name",
|
|
"site_name",
|
|
"hostname",
|
|
"description",
|
|
"user",
|
|
"patchespending",
|
|
"pendingactions",
|
|
"agentstatus",
|
|
"needs_reboot",
|
|
"last_seen",
|
|
"boot_time",
|
|
],
|
|
};
|
|
},
|
|
watch: {
|
|
search(newVal, oldVal) {
|
|
if (newVal === "") this.clearFilter();
|
|
else if (newVal.length < this.filterTextLength) this.clearFilter();
|
|
},
|
|
},
|
|
methods: {
|
|
setupWS() {
|
|
console.log("Starting websocket");
|
|
const proto = process.env.NODE_ENV === "production" || process.env.DOCKER_BUILD ? "wss" : "ws";
|
|
this.ws = new WebSocket(`${proto}://${this.wsUrl}/ws/dashinfo/?access_token=${this.token}`);
|
|
this.ws.onopen = e => {
|
|
console.log("Connected to ws");
|
|
};
|
|
this.ws.onmessage = e => {
|
|
const data = JSON.parse(e.data);
|
|
this.serverCount = data.total_server_count;
|
|
this.serverOfflineCount = data.total_server_offline_count;
|
|
this.workstationCount = data.total_workstation_count;
|
|
this.workstationOfflineCount = data.total_workstation_offline_count;
|
|
};
|
|
this.ws.onclose = e => {
|
|
console.log(`Closed code: ${e.code}`);
|
|
if (e.code !== 1000) {
|
|
setTimeout(() => {
|
|
this.setupWS();
|
|
}, 2 * 1000);
|
|
}
|
|
};
|
|
this.ws.onerror = err => {
|
|
console.log(`ERROR! Code: ${err.code}`);
|
|
this.ws.close();
|
|
};
|
|
},
|
|
toggleDark(val) {
|
|
this.$q.dark.set(val);
|
|
this.$axios.patch("/accounts/users/ui/", { dark_mode: val });
|
|
},
|
|
refreshEntireSite() {
|
|
this.$store.dispatch("loadTree");
|
|
this.getDashInfo(false);
|
|
|
|
if (this.allClientsActive) {
|
|
this.loadAllClients();
|
|
} else {
|
|
this.loadFrame(this.selectedTree, false);
|
|
}
|
|
|
|
if (this.selectedAgentPk) {
|
|
const pk = this.selectedAgentPk;
|
|
this.$store.dispatch("loadSummary", pk);
|
|
this.$store.dispatch("loadChecks", pk);
|
|
this.$store.dispatch("loadAutomatedTasks", pk);
|
|
this.$store.dispatch("loadWinUpdates", pk);
|
|
this.$store.dispatch("loadInstalledSoftware", pk);
|
|
this.$store.dispatch("loadNotes", pk);
|
|
}
|
|
},
|
|
loadFrame(activenode, destroySub = true) {
|
|
if (destroySub) this.$store.commit("destroySubTable");
|
|
|
|
let execute = false;
|
|
let urlType, id;
|
|
let data = {};
|
|
|
|
if (typeof activenode === "string") {
|
|
urlType = activenode.split("|")[0];
|
|
id = activenode.split("|")[1];
|
|
|
|
if (urlType === "Client") {
|
|
data.clientPK = id;
|
|
execute = true;
|
|
} else if (urlType === "Site") {
|
|
data.sitePK = id;
|
|
execute = true;
|
|
}
|
|
|
|
if (execute) {
|
|
this.$store.commit("AGENT_TABLE_LOADING", true);
|
|
this.$axios.patch("/agents/listagents/", data).then(r => {
|
|
this.frame = r.data;
|
|
this.$store.commit("AGENT_TABLE_LOADING", false);
|
|
});
|
|
}
|
|
}
|
|
},
|
|
getTree() {
|
|
this.loadAllClients();
|
|
this.$store.dispatch("loadTree");
|
|
},
|
|
clearTreeSelected() {
|
|
this.selectedTree = "";
|
|
this.getTree();
|
|
},
|
|
clearSite() {
|
|
this.siteActive = "";
|
|
this.$store.commit("destroySubTable");
|
|
},
|
|
loadAllClients() {
|
|
this.$store.commit("AGENT_TABLE_LOADING", true);
|
|
this.$axios.patch("/agents/listagents/").then(r => {
|
|
this.frame = r.data;
|
|
this.$store.commit("AGENT_TABLE_LOADING", false);
|
|
});
|
|
},
|
|
showPolicyAdd(node) {
|
|
this.$q
|
|
.dialog({
|
|
component: PolicyAdd,
|
|
parent: this,
|
|
type: node.children ? "client" : "site",
|
|
object: node,
|
|
})
|
|
.onOk(() => {
|
|
this.clearTreeSelected();
|
|
});
|
|
},
|
|
showAddSiteModal(node) {
|
|
this.$q.dialog({
|
|
component: SitesForm,
|
|
parent: this,
|
|
client: node.id,
|
|
});
|
|
},
|
|
showEditModal(node) {
|
|
let props = {};
|
|
if (node.children) {
|
|
props.client = { id: node.id, name: node.label };
|
|
} else {
|
|
props.site = { id: node.id, name: node.label, client: node.client };
|
|
}
|
|
|
|
this.$q.dialog({
|
|
component: node.children ? ClientsForm : SitesForm,
|
|
parent: this,
|
|
...props,
|
|
});
|
|
},
|
|
showDeleteModal(node) {
|
|
this.$q.dialog({
|
|
component: DeleteClient,
|
|
parent: this,
|
|
object: { id: node.id, name: node.label },
|
|
type: node.children ? "client" : "site",
|
|
});
|
|
},
|
|
showInstallAgent(node) {
|
|
this.sitePk = node.id;
|
|
this.showInstallAgentModal = true;
|
|
},
|
|
closeInstallAgent() {
|
|
this.showInstallAgentModal = false;
|
|
this.sitePk = null;
|
|
},
|
|
showAlertTemplateAdd(node) {
|
|
this.$q
|
|
.dialog({
|
|
component: AlertTemplateAdd,
|
|
parent: this,
|
|
type: node.children ? "client" : "site",
|
|
object: node,
|
|
})
|
|
.onOk(() => {
|
|
this.clearTreeSelected();
|
|
});
|
|
},
|
|
reload() {
|
|
this.$store.dispatch("reload");
|
|
},
|
|
livePoll() {
|
|
this.poll = setInterval(() => {
|
|
this.$store.dispatch("checkVer");
|
|
this.getDashInfo(false);
|
|
}, 60 * 5 * 1000);
|
|
},
|
|
setSplitter(val) {
|
|
this.$store.commit("SET_SPLITTER", val);
|
|
},
|
|
getDashInfo(edited = true) {
|
|
this.$store.dispatch("getDashInfo").then(r => {
|
|
if (edited) {
|
|
this.$q.loadingBar.setDefaults({ color: r.data.loading_bar_color });
|
|
this.$store.commit("SET_DEFAULT_AGENT_TBL_TAB", r.data.default_agent_tbl_tab);
|
|
this.$store.commit("SET_CLIENT_TREE_SORT", r.data.client_tree_sort);
|
|
this.$store.commit("SET_CLIENT_SPLITTER", r.data.client_tree_splitter);
|
|
}
|
|
this.darkMode = r.data.dark_mode;
|
|
this.$q.dark.set(this.darkMode);
|
|
this.currentTRMMVersion = r.data.trmm_version;
|
|
this.latestTRMMVersion = r.data.latest_trmm_ver;
|
|
this.$store.commit("SET_AGENT_DBLCLICK_ACTION", r.data.dbl_click_action);
|
|
this.$store.commit("setShowCommunityScripts", r.data.show_community_scripts);
|
|
this.$store.commit("SET_NO_CODE_SIGN", r.data.no_code_sign);
|
|
});
|
|
},
|
|
showToggleMaintenance(node) {
|
|
let data = {
|
|
id: node.id,
|
|
type: node.raw.split("|")[0],
|
|
action: node.color === "green" ? false : true,
|
|
};
|
|
|
|
const text = node.color === "green" ? "Maintenance mode was disabled" : "Maintenance mode was enabled";
|
|
this.$store
|
|
.dispatch("toggleMaintenanceMode", data)
|
|
.then(response => {
|
|
this.notifySuccess(text);
|
|
this.getTree();
|
|
})
|
|
.catch(error => {
|
|
this.notifyError("An Error occured. Please try again");
|
|
});
|
|
},
|
|
menuMaintenanceText(node) {
|
|
return node.color === "green" ? "Disable Maintenance Mode" : "Enable Maintenance Mode";
|
|
},
|
|
clearFilter() {
|
|
this.filterTextLength = 0;
|
|
this.filterPatchesPending = false;
|
|
this.filterRebootNeeded = false;
|
|
this.filterChecksFailing = false;
|
|
this.filterActionsPending = false;
|
|
this.filterAvailability = "all";
|
|
this.search = "";
|
|
},
|
|
applyFilter() {
|
|
// clear search if availability changes to all
|
|
if (
|
|
this.filterAvailability === "all" &&
|
|
(this.search.includes("is:online") || this.search.includes("is:offline") || this.search.includes("is:expired"))
|
|
)
|
|
this.clearFilter();
|
|
|
|
// don't apply filter if nothing is being filtered
|
|
if (!this.isFilteringTable) return;
|
|
|
|
let filterText = "";
|
|
|
|
if (this.filterPatchesPending) {
|
|
filterText += "is:patchespending ";
|
|
}
|
|
|
|
if (this.filterActionsPending) {
|
|
filterText += "is:actionspending ";
|
|
}
|
|
|
|
if (this.filterChecksFailing) {
|
|
filterText += "is:checksfailing ";
|
|
}
|
|
|
|
if (this.filterRebootNeeded) {
|
|
filterText += "is:rebootneeded ";
|
|
}
|
|
|
|
if (this.filterAvailability !== "all") {
|
|
if (this.filterAvailability === "online") {
|
|
filterText += "is:online ";
|
|
} else if (this.filterAvailability === "offline") {
|
|
filterText += "is:offline ";
|
|
} else if (this.filterAvailability === "offline_30days") {
|
|
filterText += "is:expired ";
|
|
}
|
|
}
|
|
|
|
this.search = filterText;
|
|
this.filterTextLength = filterText.length - 1;
|
|
},
|
|
},
|
|
computed: {
|
|
...mapState({
|
|
user: state => state.username,
|
|
clientsTree: state => state.tree,
|
|
treeReady: state => state.treeReady,
|
|
clients: state => state.clients,
|
|
}),
|
|
...mapGetters(["selectedAgentPk", "needRefresh", "clientTreeSplitterModel"]),
|
|
latestReleaseURL() {
|
|
return this.latestTRMMVersion !== "error"
|
|
? `https://github.com/wh1te909/tacticalrmm/releases/tag/v${this.latestTRMMVersion}`
|
|
: "";
|
|
},
|
|
wsUrl() {
|
|
return getBaseUrl().split("://")[1];
|
|
},
|
|
token() {
|
|
return this.$store.state.token;
|
|
},
|
|
clientTreeSplitter: {
|
|
get: function () {
|
|
return this.clientTreeSplitterModel;
|
|
},
|
|
set: function (newVal) {
|
|
this.$store.dispatch("setClientTreeSplitter", newVal);
|
|
},
|
|
},
|
|
tab: {
|
|
get: function () {
|
|
return this.$store.state.defaultAgentTblTab;
|
|
},
|
|
set: function (newVal) {
|
|
this.$store.commit("SET_DEFAULT_AGENT_TBL_TAB", newVal);
|
|
},
|
|
},
|
|
allClientsActive() {
|
|
return this.selectedTree === "";
|
|
},
|
|
filteredAgents() {
|
|
if (this.tab === "mixed") return Object.freeze(this.frame);
|
|
return Object.freeze(this.frame.filter(k => k.monitoring_type === this.tab));
|
|
},
|
|
activeNode() {
|
|
return {
|
|
client: this.clientActive,
|
|
site: this.siteActive,
|
|
};
|
|
},
|
|
isFilteringTable() {
|
|
return (
|
|
this.filterPatchesPending ||
|
|
this.filterActionsPending ||
|
|
this.filterChecksFailing ||
|
|
this.filterRebootNeeded ||
|
|
this.filterAvailability !== "all"
|
|
);
|
|
},
|
|
totalAgents() {
|
|
return this.serverCount + this.workstationCount;
|
|
},
|
|
totalOfflineAgents() {
|
|
return this.serverOfflineCount + this.workstationOfflineCount;
|
|
},
|
|
},
|
|
created() {
|
|
this.setupWS();
|
|
this.getDashInfo();
|
|
this.$store.dispatch("getUpdatedSites");
|
|
this.$store.dispatch("checkVer");
|
|
this.getTree();
|
|
},
|
|
mounted() {
|
|
this.livePoll();
|
|
},
|
|
beforeDestroy() {
|
|
this.ws.close();
|
|
clearInterval(this.poll);
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style>
|
|
.my-menu-link {
|
|
color: white;
|
|
background: lightgray;
|
|
}
|
|
</style> |