feat: 日志审计增加网站日志 (#1114)

This commit is contained in:
zhengkunwang223 2023-05-23 15:47:43 +08:00 committed by GitHub
parent 09af1e9cdf
commit 3e068a0020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 182 additions and 3 deletions

View File

@ -907,6 +907,11 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
if err := websiteRepo.Save(context.Background(), &website); err != nil {
return nil, err
}
case constant.DeleteLog:
logPath := path.Join(nginx.Install.GetPath(), "www", "sites", website.Alias, "log", req.LogType)
if err := files.NewFileOp().WriteFile(logPath, strings.NewReader(""), 0755); err != nil {
return nil, err
}
}
return res, nil
}

View File

@ -38,6 +38,7 @@ const (
GetLog = "get"
DisableLog = "disable"
EnableLog = "enable"
DeleteLog = "delete"
AccessLog = "access.log"
ErrorLog = "error.log"

View File

@ -747,6 +747,9 @@ const message = {
logout: ' logout',
},
status: 'status',
websiteLog: 'Website Log',
runLog: 'Run Log',
errLog: 'Err Log',
},
file: {
dir: 'Folder',

View File

@ -752,6 +752,9 @@ const message = {
logout: '退出',
},
status: '状态',
websiteLog: '网站日志',
runLog: '运行日志',
errLog: '错误日志',
},
file: {
dir: '文件夹',

View File

@ -37,6 +37,16 @@ const logsRouter = {
requiresAuth: false,
},
},
{
path: 'website',
name: 'WebsiteLog',
component: () => import('@/views/log/website/index.vue'),
hidden: true,
meta: {
activeMenu: '/logs',
requiresAuth: false,
},
},
{
path: 'system',
name: 'SystemLog',

View File

@ -19,5 +19,9 @@ const buttons = [
label: i18n.global.t('ssh.loginLogs'),
path: '/logs/ssh',
},
{
label: i18n.global.t('logs.websiteLog'),
path: '/logs/website',
},
];
</script>

View File

@ -0,0 +1,153 @@
<template>
<div>
<LayoutContent v-loading="loading" :title="$t('logs.websiteLog')">
<template #toolbar>
<el-row>
<el-col :span="16">
<el-radio-group v-model="logReq.logType" @change="search()">
<el-radio-button :label="'access.log'">
{{ $t('logs.runLog') }}
</el-radio-button>
<el-radio-button :label="'error.log'" style="margin-left: 10px">
{{ $t('logs.errLog') }}
</el-radio-button>
</el-radio-group>
</el-col>
</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>
</template>
<template #main>
<Codemirror
style="height: calc(100vh - 430px); width: 100%"
:autofocus="true"
:placeholder="$t('website.noLog')"
:indent-with-tab="true"
:tabSize="4"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="data.content"
:disabled="true"
@ready="handleReady"
/>
</template>
</LayoutContent>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmitClean"></ConfirmDialog>
</div>
</template>
<script setup lang="ts">
import { ListWebsites, OpWebsiteLog } from '@/api/modules/website';
import { nextTick, reactive, shallowRef } from 'vue';
import { onMounted } from 'vue';
import { ref } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
const extensions = [javascript(), oneDark];
const loading = ref(false);
const websites = ref();
const logReq = reactive({
id: 0,
operate: 'get',
logType: 'access.log',
});
const data = ref({
enable: false,
content: '',
});
const confirmDialogRef = ref();
const getWebsites = async () => {
loading.value = true;
await ListWebsites()
.then((res) => {
websites.value = res.data || [];
if (websites.value.length > 0) {
logReq.id = websites.value[0].id;
search();
}
})
.finally(() => {
loading.value = false;
});
};
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
const search = () => {
loading.value = true;
OpWebsiteLog(logReq)
.then((res) => {
data.value = res.data;
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
});
});
})
.finally(() => {
loading.value = false;
});
};
const onClean = async () => {
const params = {
header: i18n.global.t('logs.deleteLogs'),
operationInfo: i18n.global.t('commons.msg.delete'),
submitInputInfo: i18n.global.t('logs.deleteLogs'),
};
confirmDialogRef.value!.acceptParams(params);
};
const onSubmitClean = async () => {
search();
const req = {
id: logReq.id,
operate: 'delete',
logType: logReq.logType,
};
loading.value = true;
OpWebsiteLog(req)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.finally(() => {
loading.value = false;
});
};
onMounted(() => {
getWebsites();
});
</script>

View File

@ -61,12 +61,12 @@ const logType = computed(() => {
const id = computed(() => {
return props.id;
});
let loading = ref(false);
let data = ref({
const loading = ref(false);
const data = ref({
enable: false,
content: '',
});
let tailLog = ref(false);
const tailLog = ref(false);
let timer: NodeJS.Timer | null = null;
const view = shallowRef();