Files
tacticalrmm-web/src/components/modals/agents/WebsocketSendCommand.vue
2022-06-26 06:59:59 +00:00

212 lines
5.5 KiB
Vue

<template>
<q-dialog
ref="dialogRef"
@hide="onDialogHide"
persistent
@keydown.esc="onDialogHide"
>
<q-card
class="q-dialog-plugin"
:style="{ 'min-width': !ret ? '40vw' : '70vw' }"
>
<q-bar>
Send command on {{ agent.hostname }}
<q-space />
<q-chip v-if="!wsConnected" color="red" text-color="white" icon="error"
>Websocket diconnected!</q-chip
>
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-form @submit="submit">
<q-card-section>
<p>Shell</p>
<div class="q-gutter-sm">
<q-radio
v-if="agent.plat !== 'windows'"
dense
v-model="state.shell"
val="/bin/bash"
label="Bash"
@update:model-value="state.custom_shell = null"
/>
<q-radio
v-if="agent.plat !== 'windows'"
dense
v-model="state.shell"
val="custom"
label="Custom"
/>
<q-radio
v-if="agent.plat === 'windows'"
dense
v-model="state.shell"
val="cmd"
label="CMD"
/>
<q-radio
v-if="agent.plat === 'windows'"
dense
v-model="state.shell"
val="powershell"
label="Powershell"
/>
</div>
</q-card-section>
<q-card-section v-if="state.shell === 'custom'">
<q-input
v-model="state.custom_shell"
outlined
label="Custom shell"
stack-label
placeholder="/usr/bin/python3"
:rules="[(val) => !!val || '*Required']"
/>
</q-card-section>
<q-card-section>
<q-input
v-model.number="state.timeout"
dense
outlined
type="number"
style="max-width: 150px"
label="Timeout (seconds)"
stack-label
:rules="[
(val) => !!val || '*Required',
(val) => val >= 10 || 'Minimum is 10 seconds',
(val) => val <= 3600 || 'Maximum is 3600 seconds',
]"
/>
</q-card-section>
<q-card-section>
<q-input
v-model="state.cmd"
outlined
label="Command"
stack-label
:placeholder="cmdPlaceholder(state.shell)"
:rules="[(val) => !!val || '*Required']"
/>
</q-card-section>
<q-card-actions align="right">
<q-btn flat dense push label="Cancel" v-close-popup />
<q-btn
:loading="loading"
:disable="!wsConnected"
flat
dense
push
label="Send"
color="primary"
type="submit"
>
</q-btn>
</q-card-actions>
<q-card-section
v-if="ret !== null"
class="q-pl-md q-pr-md q-pt-none q-ma-none scroll"
style="max-height: 50vh"
>
<pre>{{ ret }}</pre>
</q-card-section>
</q-form>
</q-card>
</q-dialog>
</template>
<script>
// composition imports
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { useStore } from "vuex";
import { useDialogPluginComponent } from "quasar";
import { cmdPlaceholder } from "@/composables/agents";
import { getWSUrl } from "@/websocket/channels";
export default {
name: "SendCommand",
emits: [...useDialogPluginComponent.emits],
props: {
agent: !Object,
},
setup(props) {
const store = useStore();
// setup quasar dialog plugin
const { dialogRef, onDialogHide } = useDialogPluginComponent();
// run command logic
const state = ref({
shell: props.agent.plat === "windows" ? "cmd" : "/bin/bash",
cmd: null,
timeout: 30,
custom_shell: null,
agent_id: props.agent.agent_id,
});
const loading = ref(false);
const ret = ref(null);
// websocket
const ws = ref(null);
const wsConnected = ref(false);
function setupWS() {
const token = computed(() => store.state.token);
console.log("Starting send command websocket");
let url = getWSUrl("sendcmd", token.value);
ws.value = new WebSocket(url);
ws.value.onopen = () => {
wsConnected.value = true;
console.log("Send command websocket connected");
};
ws.value.onmessage = (e) => {
const data = JSON.parse(e.data);
ret.value = data.ret;
loading.value = false;
};
ws.value.onclose = () => {
console.log("Send command websocket disconnected");
wsConnected.value = false;
};
ws.value.onerror = () => {
wsConnected.value = false;
console.log("Send command websocket error");
ws.value.onclose();
};
}
function submit() {
ret.value = null;
loading.value = true;
ret.value = ws.value.send(JSON.stringify(state.value));
}
onMounted(() => {
setupWS();
});
onBeforeUnmount(() => {
ws.value.close();
});
return {
// reactive data
state,
loading,
ret,
wsConnected,
// methods
submit,
cmdPlaceholder,
// quasar dialog
dialogRef,
onDialogHide,
};
},
};
</script>