Files
tacticalrmm-web/src/views/Dashboard.vue
2020-10-15 19:53:33 -04:00

439 lines
13 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</q-toolbar-title>
<AlertsIcon />
<q-btn-dropdown flat no-caps stretch :label="user">
<q-list>
<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 :clients="clients" @edited="refreshEntireSite" />
<q-splitter v-model="outsideModel">
<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">
<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 clickable v-close-popup @click="showPolicyAdd(props.node)">
<q-item-section side>
<q-icon name="policy" />
</q-item-section>
<q-item-section>Edit Policies</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" label="Search" dense outlined clearable class="q-pr-md q-pb-xs">
<template v-slot:prepend>
<q-icon name="search" color="primary" />
</template>
</q-input>
</div>
<AgentTable
:frame="frame"
:columns="columns"
:tab="tab"
:filter="filteredAgents"
:userName="user"
:search="search"
@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 />
</template>
</q-splitter>
</template>
</q-splitter>
</q-page-container>
<!-- edit client modal -->
<q-dialog v-model="showEditClientModal">
<EditClients @close="showEditClientModal = false" @edited="refreshEntireSite" />
</q-dialog>
<!-- edit site modal -->
<q-dialog v-model="showEditSiteModal">
<EditSites @close="showEditSiteModal = false" @edited="refreshEntireSite" />
</q-dialog>
<!-- delete client modal -->
<q-dialog v-model="showDeleteClientModal">
<DeleteClient @close="showDeleteClientModal = false" @edited="refreshEntireSite" />
</q-dialog>
<!-- delete site modal -->
<q-dialog v-model="showDeleteSiteModal">
<DeleteSite @close="showDeleteSiteModal = false" @edited="refreshEntireSite" />
</q-dialog>
<!-- add policy modal -->
<q-dialog v-model="showPolicyAddModal">
<PolicyAdd @close="showPolicyAddModal = false" :type="policyAddType" :pk="parseInt(policyAddPk)" />
</q-dialog>
</q-layout>
</template>
<script>
import axios from "axios";
import { mapState, mapGetters } from "vuex";
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 EditSites from "@/components/modals/clients/EditSites";
import EditClients from "@/components/modals/clients/EditClients";
import DeleteClient from "@/components/modals/clients/DeleteClient";
import DeleteSite from "@/components/modals/clients/DeleteSite";
export default {
components: {
FileBar,
AgentTable,
SubTableTabs,
AlertsIcon,
PolicyAdd,
EditSites,
EditClients,
DeleteClient,
DeleteSite,
},
data() {
return {
showEditClientModal: false,
showEditSiteModal: false,
showDeleteClientModal: false,
showDeleteSiteModal: false,
showPolicyAddModal: false,
policyAddType: null,
policyAddPk: null,
outsideModel: 11,
selectedTree: "",
innerModel: 50,
tab: "server",
clientActive: "",
siteActive: "",
frame: [],
poll: null,
search: null,
columns: [
{
name: "smsalert",
align: "left",
},
{
name: "emailalert",
align: "left",
},
{
name: "checks-status",
align: "left",
},
{
name: "client",
label: "Client",
field: "client",
sortable: true,
align: "left",
},
{
name: "site",
label: "Site",
field: "site",
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_in_username",
sortable: true,
align: "left",
},
{
name: "patchespending",
align: "left",
},
/* {
name: "antivirus",
align: "left"
}, */
{
name: "agentstatus",
field: "status",
align: "left",
},
{
name: "needsreboot",
field: "needs_reboot",
align: "left",
},
{
name: "lastseen",
label: "Last Response",
field: "last_seen",
sortable: true,
align: "left",
},
{
name: "boottime",
label: "Boot Time",
field: "boot_time",
sortable: true,
align: "left",
},
],
};
},
methods: {
refreshEntireSite() {
this.$store.dispatch("loadTree");
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 client, site, url;
try {
client = this.$refs.tree.meta[activenode].parent.key.split("|")[1];
site = activenode.split("|")[1];
url = `/agents/bysite/${client}/${site}/`;
} catch (e) {
try {
client = activenode.split("|")[1];
} catch (e) {
return false;
}
if (client === null || client === undefined) {
url = null;
} else {
url = `/agents/byclient/${client}/`;
}
}
if (url) {
this.$store.commit("AGENT_TABLE_LOADING", true);
axios.get(url).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);
axios.get("/agents/listagents/").then(r => {
this.frame = r.data;
//this.siteActive = "";
//this.$store.commit("destroySubTable");
this.$store.commit("AGENT_TABLE_LOADING", false);
});
},
showPolicyAdd(node) {
if (node.children) {
this.policyAddType = "client";
this.policyAddPk = node.id;
this.showPolicyAddModal = true;
} else {
this.policyAddType = "site";
this.policyAddPk = node.id;
this.showPolicyAddModal = true;
}
},
showEditModal(node) {
if (node.children) {
this.showEditClientModal = true;
} else {
this.showEditSiteModal = true;
}
},
showDeleteModal(node) {
if (node.children) {
this.showDeleteClientModal = true;
} else {
this.showDeleteSiteModal = true;
}
},
reload() {
this.$store.dispatch("reload");
},
livePoll() {
this.poll = setInterval(() => {
this.$store.dispatch("checkVer");
}, 60 * 5 * 1000);
},
setSplitter(val) {
this.$store.commit("SET_SPLITTER", val);
},
},
computed: {
...mapState({
user: state => state.username,
clientsTree: state => state.tree,
treeReady: state => state.treeReady,
clients: state => state.clients,
}),
...mapGetters(["selectedAgentPk", "needRefresh"]),
allClientsActive() {
return this.selectedTree === "" ? true : false;
},
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,
};
},
},
created() {
this.getTree();
this.$store.dispatch("getUpdatedSites");
this.$store.dispatch("checkVer");
},
mounted() {
this.loadFrame(this.activeNode);
this.livePoll();
},
beforeDestroy() {
clearInterval(this.poll);
},
};
</script>
<style>
.my-menu-link {
color: white;
background: lightgray;
}
</style>