Move logs.html from frontend to dashboard
This commit is contained in:
226
dashboard/src/components/LogsViewer.vue
Normal file
226
dashboard/src/components/LogsViewer.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<MainLayout>
|
||||
<template #dialogs>
|
||||
</template>
|
||||
<template #header>
|
||||
<TopBar class="navbar">
|
||||
<template #left>
|
||||
<span class="title">{{ name }}</span>
|
||||
</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="showRestart" secondary tool :loading="busyRestart" icon="fa-solid fa-arrows-rotate" @click="onRestartApp"/>
|
||||
<Button :href="'/frontend/terminal.html?id=' + id" target="_blank" v-show="showTerminal" secondary tool icon="fa-solid fa-terminal" :title="$t('terminal.title')" />
|
||||
<Button :href="'/frontend/filemanager.html#/home/app/' + id" target="_blank" v-show="showFilemanager" secondary tool icon="fa-solid fa-folder" :title="$t('filemanager.title')" />
|
||||
</template>
|
||||
</TopBar>
|
||||
</template>
|
||||
<template #body>
|
||||
<div ref="linesContainer"></div>
|
||||
<div class="bottom-spacer"></div>
|
||||
</template>
|
||||
</MainLayout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { Button, TopBar, MainLayout } from 'pankow';
|
||||
|
||||
import LogsModel from '../models/LogsModel.js';
|
||||
import AppModel from '../models/AppModel.js';
|
||||
|
||||
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? import.meta.env.VITE_API_ORIGIN : window.location.origin;
|
||||
|
||||
export default {
|
||||
name: 'LogsViewer',
|
||||
components: {
|
||||
Button,
|
||||
MainLayout,
|
||||
TopBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
accessToken: localStorage.token,
|
||||
logsModel: null,
|
||||
appModel: null,
|
||||
busyRestart: false,
|
||||
showRestart: false,
|
||||
showFilemanager: false,
|
||||
showTerminal: false,
|
||||
id: '',
|
||||
name: '',
|
||||
type: '',
|
||||
downloadUrl: '',
|
||||
logLines: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onClear() {
|
||||
while (this.$refs.linesContainer.firstChild) this.$refs.linesContainer.removeChild(this.$refs.linesContainer.firstChild);
|
||||
},
|
||||
onDownload() {
|
||||
this.logsModel.download();
|
||||
},
|
||||
async onRestartApp() {
|
||||
if (this.type !== 'app') return;
|
||||
|
||||
this.busyRestart = true;
|
||||
|
||||
await this.appModel.restart();
|
||||
|
||||
this.busyRestart = false;
|
||||
}
|
||||
},
|
||||
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;
|
||||
}
|
||||
|
||||
this.logsModel = LogsModel.create(API_ORIGIN, this.accessToken, this.type, this.id);
|
||||
|
||||
if (this.type === 'app') {
|
||||
this.appModel = AppModel.create(API_ORIGIN, this.accessToken, this.id);
|
||||
|
||||
try {
|
||||
const app = await this.appModel.get();
|
||||
this.name = `${app.label || app.fqdn} (${app.manifest.title})`;
|
||||
this.showFilemanager = !!app.manifest.addons.localstorage;
|
||||
this.showTerminal = app.manifest.id !== 'io.cloudron.builtin.appproxy';
|
||||
this.showRestart = app.manifest.id !== 'io.cloudron.builtin.appproxy';
|
||||
} catch (e) {
|
||||
console.error(`Failed to get app info for ${this.id}:`, e);
|
||||
}
|
||||
}
|
||||
|
||||
window.document.title = `Logs Viewer - ${this.name}`;
|
||||
|
||||
this.downloadUrl = this.logsModel.getDownloadUrl();
|
||||
|
||||
const maxLines = 1000;
|
||||
let lines = 0;
|
||||
let newLogLines = [];
|
||||
|
||||
const tmp = document.getElementsByClassName('pankow-main-layout-body')[0];
|
||||
setInterval(() => {
|
||||
newLogLines = newLogLines.slice(-maxLines)
|
||||
|
||||
for (let line of newLogLines) {
|
||||
if (lines < maxLines) ++lines;
|
||||
else this.$refs.linesContainer.removeChild(this.$refs.linesContainer.firstChild);
|
||||
|
||||
const logLine = document.createElement('div');
|
||||
logLine.className = 'log-line';
|
||||
logLine.innerHTML = `<span class="time">${line.time || '[no timestamp] ' }</span> <span>${line.html}</span>`;
|
||||
this.$refs.linesContainer.appendChild(logLine);
|
||||
|
||||
const autoScroll = tmp.scrollTop > (tmp.scrollHeight - tmp.clientHeight - 34);
|
||||
if (autoScroll) setTimeout(() => tmp.scrollTop = tmp.scrollHeight, 1);
|
||||
}
|
||||
|
||||
newLogLines = [];
|
||||
}, 500);
|
||||
|
||||
this.logsModel.stream((time, html) => {
|
||||
newLogLines.push({ time, html });
|
||||
}, function (error) {
|
||||
newLogLines.push({ time: error.time, html: error.html });
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.pankow-main-layout-body {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
@media (max-width: 641px) {
|
||||
.hide-phone {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pankow-top-bar {
|
||||
background-color: black;
|
||||
color: white;
|
||||
margin-bottom: 0 !important;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.bottom-spacer {
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user