Files
watchstate/frontend/pages/console.vue
2024-05-17 21:26:25 +03:00

177 lines
6.2 KiB
Vue

<template>
<div class="columns is-multiline">
<div class="column is-12 is-clearfix">
<h1 class="title is-4">Console</h1>
<div class="subtitle">
You can execute <strong>non-interactive</strong> commands here. This interface is jailed to <code>console</code>
command.
</div>
</div>
<div class="column is-12">
<form @submit.prevent="RunCommand">
<div class="field">
<div class="field-body">
<div class="field is-grouped-tablet">
<p class="control is-expanded has-icons-left">
<input type="text" class="input" v-model="command" placeholder="system:view" autocomplete="off"
autofocus
:disabled="isLoading">
<span class="icon is-left">
<i class="fas fa-terminal"></i>
</span>
</p>
<p class="control">
<button class="button is-primary" type="submit" :disabled="isLoading ||hasPrefix"
:class="{'is-loading':isLoading}">
<span class="icon-text">
<span class="icon">
<i class="fa fa-server"></i>
</span>
<span>Run</span>
</span>
</button>
</p>
<p class="control">
<button class="button is-info" type="button" v-tooltip="'Clear output'"
@click="response = []" :disabled="response.length < 1">
<span class="icon-text">
<span class="icon"><i class="fa fa-broom"></i></span>
<span>Clear</span>
</span>
</button>
</p>
<p class="control" v-if="isLoading">
<button class="button is-danger" type="button" @click="finished" v-tooltip="'Close connection.'">
<span class="icon-text">
<span class="icon"><i class="fa fa-power-off"></i></span>
<span>Close Connection</span>
</span>
</button>
</p>
</div>
</div>
<p class="help" v-if="hasPrefix">
<span class="icon-text">
<span class="icon has-text-danger"><i class="fas fa-exclamation-triangle"></i></span>
<span>Remove the <code>console</code> or <code>docker exec -ti watchstate console</code> from the
input. You should use the command directly, For example i.e <code>db:list --output yaml</code></span>
</span>
</p>
</div>
</form>
</div>
<div class="column is-12">
<pre ref="outputConsole" style="min-height: 60vh;max-height:80vh; overflow-y: scroll"
><code><span v-for="(item, index) in response" :key="'log_line-'+index" class="is-block">{{
item
}}</span></code></pre>
</div>
<div class="column is-12" v-if="show_page_tips">
<Message title="Tips" message_class="has-background-info-90 has-text-dark">
<button class="delete" @click="show_page_tips=false"></button>
<div class="content">
<ul>
<li>
You can also run a command from the task page by clicking on the <strong>Run via console</strong>. The
command will be pre-filled for you.
</li>
<li>
Clicking close connection does not stop the command. It only stops the output from being displayed. The
command will continue to run until it finishes.
</li>
<li>
The majority of the commands will not show any output unless error has occurred or important information
needs to be communicated. To see all output, add the <code>-vvv</code> flag.
</li>
<li>
There is no need to write <code>console</code> or <code>docker exec -ti watchstate console</code> Using
this interface. Use the command followed by the options directly. For example, <code>db:list --output
yaml</code>.
</li>
</ul>
</div>
</Message>
</div>
</div>
</template>
<script setup>
import {useStorage} from "@vueuse/core";
import {notification} from "~/utils/index.js";
const fromTask = useRoute().query.task || '';
useHead({title: `Console`})
let sse;
const response = ref([]);
const command = ref('');
const isLoading = ref(false);
const outputConsole = ref();
const hasPrefix = computed(() => command.value.startsWith('console') || command.value.startsWith('docker'));
const show_page_tips = useStorage('show_page_tips', true)
const RunCommand = async () => {
const api_path = useStorage('api_path', '/v1/api')
const api_url = useStorage('api_url', '')
const api_token = useStorage('api_token', '')
let userCommand = command.value
// -- check if the user command starts with console or docker exec -ti watchstate
if (userCommand.startsWith('console') || userCommand.startsWith('docker')) {
notification('error', 'Warning', 'Please remove the [console] or [docker exec -ti watchstate console] from the command.')
return
}
response.value = []
const searchParams = new URLSearchParams();
searchParams.append('apikey', api_token.value);
searchParams.append('json', btoa(JSON.stringify({command: userCommand})));
sse = new EventSource(`${api_url.value}${api_path.value}/system/command/?${searchParams.toString()}`);
isLoading.value = true;
sse.addEventListener('data', async e => {
let lines = e.data.split(/\n/g);
for (let x = 0; x < lines.length; x++) {
response.value.push(lines[x]);
}
});
sse.addEventListener('close', () => finished());
sse.onclose = () => finished();
sse.onerror = () => finished();
}
const finished = () => {
if (sse) {
sse.close();
}
isLoading.value = false;
}
onUpdated(() => outputConsole.value.scrollTop = outputConsole.value.scrollHeight);
onMounted(async () => {
if (!fromTask) {
await RunCommand();
return
}
const response = await request(`/tasks/${fromTask}`);
const json = await response.json();
command.value = `${json.command} ${json.args || ''}`;
await RunCommand();
});
</script>