Files
cloudron-box/frontend/src/components/LogsViewer.vue

205 lines
5.0 KiB
Vue
Raw Normal View History

2023-07-12 13:21:21 +02:00
<template>
<MainLayout>
<template #dialogs>
</template>
<template #header>
<TopBar class="navbar">
<template #left>
2023-07-12 20:44:56 +02:00
<span class="title">{{ name }}</span>
2023-07-12 13:21:21 +02:00
</template>
<template #right>
<Button icon="fa-solid fa-eraser" @click="onClear()" style="margin-right: 5px">{{ $t('logs.clear') }}</Button>
<Button :href="downloadUrl" target="_blank" icon="fa-solid fa-download">{{ $t('logs.download') }}</Button>
<Button style="margin-left: 20px;" :title="$t('filemanager.toolbar.restartApp')" v-show="type === 'app'" secondary :loading="busyRestart" icon="fa-solid fa-arrows-rotate" @click="onRestartApp"/>
<Button :href="'/frontend/terminal.html?id=' + id" target="_blank" v-show="type === 'app'" secondary icon="fa-solid fa-terminal" :title="$t('terminal.title')" />
<Button :href="'/frontend/filemanager.html#/home/app/' + id" target="_blank" v-show="type === 'app'" secondary icon="fa-solid fa-folder" :title="$t('filemanager.title')" />
2023-07-12 13:21:21 +02:00
</template>
</TopBar>
</template>
<template #body>
<div v-for="line of logLines" class="log-line">
<span class="time">{{ line.time || '[no timestamp]&nbsp;' }}</span> <span v-html="line.html"></span>
2023-07-12 13:21:21 +02:00
</div>
<div class="bottom-spacer"></div>
2023-07-12 13:21:21 +02:00
</template>
</MainLayout>
</template>
<script>
import { Button, TopBar, MainLayout } from 'pankow';
2023-07-12 13:21:21 +02:00
2023-07-14 14:48:43 +02:00
import LogsModel from '../models/LogsModel.js';
import AppModel from '../models/AppModel.js';
2023-07-12 13:21:21 +02:00
2023-07-14 18:34:22 +02:00
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? 'https://' + import.meta.env.VITE_API_ORIGIN : window.location.origin ;
2023-07-12 13:21:21 +02:00
export default {
name: 'LogsViewer',
components: {
Button,
MainLayout,
TopBar
},
data() {
return {
accessToken: localStorage.token,
apiOrigin: API_ORIGIN || '',
logsModel: null,
2023-07-14 14:48:43 +02:00
appModel: null,
busyRestart: false,
2023-07-12 13:21:21 +02:00
id: '',
name: '',
type: '',
downloadUrl: '',
logLines: []
};
},
methods: {
onClear() {
this.logLines = [];
},
onDownload() {
this.logsModel.download();
},
async onRestartApp() {
if (this.type !== 'app') return;
this.busyRestart = true;
2023-07-14 14:48:43 +02:00
await this.appModel.restart();
this.busyRestart = false;
2023-07-12 13:21:21 +02:00
}
},
async mounted() {
if (!localStorage.token) {
console.error('Set localStorage.token');
return;
}
const urlParams = new URLSearchParams(window.location.search);
const appId = urlParams.get('appId');
const taskId = urlParams.get('taskId');
const crashId = urlParams.get('crashId');
const id = urlParams.get('id');
if (appId) {
this.type = 'app';
this.id = appId;
this.name = 'App ' + appId;
} else if (taskId) {
this.type = 'task';
this.id = taskId;
this.name = 'Task ' + taskId;
} else if (crashId) {
this.type = 'crash';
this.id = crashId;
this.name = 'Crash ' + crashId;
} else if (id) {
if (id === 'box') {
this.type = 'platform';
this.id = id;
this.name = 'Box';
} else {
this.type = 'service';
this.id = id;
this.name = 'Service ' + id;
}
} else {
console.error('no supported log type specified');
return;
}
2023-07-14 14:48:43 +02:00
this.logsModel = LogsModel.create(this.apiOrigin, this.accessToken, this.type, this.id);
2023-07-12 13:21:21 +02:00
if (this.type === 'app') {
2023-07-14 14:48:43 +02:00
this.appModel = AppModel.create(this.apiOrigin, this.accessToken, this.id);
2023-07-12 13:21:21 +02:00
try {
2023-07-14 14:48:43 +02:00
const app = await this.appModel.get();
this.name = `${app.label || app.fqdn} (${app.manifest.title})`;
2023-07-12 13:21:21 +02:00
} catch (e) {
console.error(`Failed to get app info for ${this.id}:`, e);
}
}
window.document.title = `Logs Viewer - ${this.name}`;
2023-07-12 13:21:21 +02:00
this.downloadUrl = this.logsModel.getDownloadUrl();
this.logsModel.stream((time, html) => {
this.logLines.push({ time, html});
2023-07-12 13:21:21 +02:00
2023-07-12 14:16:48 +02:00
const tmp = document.getElementsByClassName('cloudron-layout-body')[0];
if (!tmp) return;
2023-07-12 13:21:21 +02:00
const autoScroll = tmp.scrollTop > (tmp.scrollHeight - tmp.clientHeight - 34);
if (autoScroll) setTimeout(() => tmp.scrollTop = tmp.scrollHeight, 1);
2023-07-12 13:21:21 +02:00
}, function (error) {
console.error('Failed to start log stream:', error);
})
}
};
</script>
<style>
body {
background-color: black;
}
2023-07-12 20:44:56 +02:00
.title {
font-size: 20px;
}
2023-07-12 13:21:21 +02:00
.cloudron-layout-body {
cursor: text;
}
@media (max-width: 641px) {
.hide-phone {
display: none !important;
}
}
2023-07-12 13:21:21 +02:00
.cloudron-top {
background-color: black;
color: white;
margin-bottom: 0 !important;
padding: 5px 10px;
2023-07-12 13:21:21 +02:00
}
.log-line {
color: white;
font-family: monospace;
font-size: 14px;
line-height: 1.2;
white-space: nowrap;
width: 100%;
}
.log-line:hover {
background-color: #333333;
}
.log-line > .time {
color: #0ff;
position: sticky;
left: 0;
margin-right: 10px;
background-color: black;
}
.log-line:hover > .time {
background-color: #333333;
}
2023-07-12 13:21:21 +02:00
.bottom-spacer {
height: 5px;
}
</style>