feat: 日志审计-网站日志增加下载和追踪功能 (#1592)

日志审计-网站日志增加下载和追踪功能
pull/1603/head
zhengkunwang223 2023-07-10 21:27:07 +08:00 committed by GitHub
parent f9b93e8c85
commit ac55385696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 50 deletions

View File

@ -47,7 +47,7 @@
<script lang="ts" setup>
import { cleanContainerLog } from '@/api/modules/container';
import i18n from '@/lang';
import { dateFormatForName } from '@/utils/util';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import { onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
@ -115,13 +115,7 @@ const searchLogs = async () => {
};
const onDownload = async () => {
const downloadUrl = window.URL.createObjectURL(new Blob([logInfo.value]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = logSearch.container + '-' + dateFormatForName(new Date()) + '.log';
const event = new MouseEvent('click');
a.dispatchEvent(event);
downloadWithContent(logInfo.value, logSearch.container + '-' + dateFormatForName(new Date()) + '.log');
};
interface DialogProps {

View File

@ -342,3 +342,7 @@
.mini-form-item {
width: 40% !important;
}
.left-button {
margin-left: 10px;
}

View File

@ -283,3 +283,13 @@ export function downloadFile(filePath: string) {
let url = `${import.meta.env.VITE_API_URL as string}/files/download?`;
window.open(url + 'path=' + filePath, '_blank');
}
export function downloadWithContent(content: string, fileName: string) {
const downloadUrl = window.URL.createObjectURL(new Blob([content]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = fileName;
const event = new MouseEvent('click');
a.dispatchEvent(event);
}

View File

@ -68,7 +68,7 @@
<script lang="ts" setup>
import { cleanContainerLog } from '@/api/modules/container';
import i18n from '@/lang';
import { dateFormatForName } from '@/utils/util';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import { computed, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
@ -157,13 +157,7 @@ const searchLogs = async () => {
};
const onDownload = async () => {
const downloadUrl = window.URL.createObjectURL(new Blob([logInfo.value]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = logSearch.container + '-' + dateFormatForName(new Date()) + '.log';
const event = new MouseEvent('click');
a.dispatchEvent(event);
downloadWithContent(logInfo.value, logSearch.container + '-' + dateFormatForName(new Date()) + '.log');
};
const onClean = async () => {

View File

@ -54,7 +54,7 @@ import { Database } from '@/api/interface/database';
import { LoadFile } from '@/api/modules/files';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { updateMysqlVariables } from '@/api/modules/database';
import { dateFormatForName } from '@/utils/util';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import i18n from '@/lang';
import { loadBaseDir } from '@/api/modules/setting';
import { MsgError, MsgInfo, MsgSuccess } from '@/utils/message';
@ -165,13 +165,7 @@ const onDownload = async () => {
MsgInfo(i18n.global.t('database.noData'));
return;
}
const downloadUrl = window.URL.createObjectURL(new Blob([slowLogs.value]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = mysqlName.value + '-slowlogs-' + dateFormatForName(new Date()) + '.log';
const event = new MouseEvent('click');
a.dispatchEvent(event);
downloadWithContent(slowLogs.value, mysqlName.value + '-slowlogs-' + dateFormatForName(new Date()) + '.log');
};
const loadMysqlSlowlogs = async (path: string) => {

View File

@ -24,24 +24,34 @@
</el-row>
</template>
<template #search>
<el-select v-model="logReq.id" @change="search()">
<template #prefix>{{ $t('website.website') }}</template>
<el-option
v-for="(website, index) in websites"
:key="index"
:label="website.primaryDomain"
:value="website.id"
></el-option>
</el-select>
<el-button
type="primary"
plain
@click="onClean()"
style="margin-left: 10px"
:disabled="data.content.length === 0"
>
{{ $t('logs.deleteLogs') }}
</el-button>
<div>
<el-select v-model="logReq.id" @change="search()">
<template #prefix>{{ $t('website.website') }}</template>
<el-option
v-for="(website, index) in websites"
:key="index"
:label="website.primaryDomain"
:value="website.id"
></el-option>
</el-select>
<el-button class="left-button">
<el-checkbox v-model="tailLog" @change="changeTail">
{{ $t('commons.button.watch') }}
</el-checkbox>
</el-button>
<el-button class="left-button" @click="onDownload" icon="Download" :disabled="data.content === ''">
{{ $t('file.download') }}
</el-button>
<el-button
type="primary"
plain
@click="onClean()"
class="left-button"
:disabled="data.content.length === 0"
>
{{ $t('logs.deleteLogs') }}
</el-button>
</div>
</template>
<template #main>
<Codemirror
@ -74,6 +84,7 @@ import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
const extensions = [javascript(), oneDark];
@ -89,6 +100,8 @@ const data = ref({
content: '',
});
const confirmDialogRef = ref();
const tailLog = ref(false);
let timer: NodeJS.Timer | null = null;
const getWebsites = async () => {
loading.value = true;
@ -144,6 +157,26 @@ const onClean = async () => {
confirmDialogRef.value!.acceptParams(params);
};
const onDownload = async () => {
downloadWithContent(data.value.content, logReq.logType + '-' + dateFormatForName(new Date()) + '.log');
};
const changeTail = () => {
if (tailLog.value) {
timer = setInterval(() => {
search();
}, 1000 * 5);
} else {
onCloseLog();
}
};
const onCloseLog = async () => {
tailLog.value = false;
clearInterval(Number(timer));
timer = null;
};
const onSubmitClean = async () => {
search();
const req = {

View File

@ -45,7 +45,7 @@ import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { computed, nextTick, onMounted, onUnmounted, ref, shallowRef } from 'vue';
import { OpWebsiteLog } from '@/api/modules/website';
import { dateFormatForName } from '@/utils/util';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import { useDeleteData } from '@/hooks/use-delete-data';
const extensions = [javascript(), oneDark];
@ -143,13 +143,7 @@ const cleanLog = async () => {
};
const onDownload = async () => {
const downloadUrl = window.URL.createObjectURL(new Blob([data.value.content]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = logType.value + '-' + dateFormatForName(new Date()) + '.log';
const event = new MouseEvent('click');
a.dispatchEvent(event);
downloadWithContent(data.value.content, logType.value + '-' + dateFormatForName(new Date()) + '.log');
};
const onCloseLog = async () => {