177 lines
6.2 KiB
Vue
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:70vh; 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>
|