1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-02-07 17:10:07 +08:00

feat: SSH 日志统计信息增加分页 (#2509)

This commit is contained in:
ssongliu 2023-10-11 17:20:30 +08:00 committed by GitHub
parent faf4174adf
commit c01b3764f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 111 additions and 79 deletions

View File

@ -38,9 +38,17 @@ type SSHLog struct {
} }
type SearchForAnalysis struct { type SearchForAnalysis struct {
PageInfo
OrderBy string `json:"orderBy" validate:"required,oneof=Success Failed"` OrderBy string `json:"orderBy" validate:"required,oneof=Success Failed"`
} }
type AnalysisRes struct {
Total int64 `json:"total"`
Items []SSHLogAnalysis `json:"items"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
}
type SSHLogAnalysis struct { type SSHLogAnalysis struct {
Address string `json:"address"` Address string `json:"address"`
Area string `json:"area"` Area string `json:"area"`

View File

@ -32,7 +32,7 @@ type ISSHService interface {
UpdateByFile(value string) error UpdateByFile(value string) error
Update(key, value string) error Update(key, value string) error
GenerateSSH(req dto.GenerateSSH) error GenerateSSH(req dto.GenerateSSH) error
AnalysisLog(req dto.SearchForAnalysis) ([]dto.SSHLogAnalysis, error) AnalysisLog(req dto.SearchForAnalysis) (*dto.AnalysisRes, error)
LoadSSHSecret(mode string) (string, error) LoadSSHSecret(mode string) (string, error)
LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error)
@ -304,7 +304,7 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
return &data, nil return &data, nil
} }
func (u *SSHService) AnalysisLog(req dto.SearchForAnalysis) ([]dto.SSHLogAnalysis, error) { func (u *SSHService) AnalysisLog(req dto.SearchForAnalysis) (*dto.AnalysisRes, error) {
var fileList []string var fileList []string
baseDir := "/var/log" baseDir := "/var/log"
if err := filepath.Walk(baseDir, func(pathItem string, info os.FileInfo, err error) error { if err := filepath.Walk(baseDir, func(pathItem string, info os.FileInfo, err error) error {
@ -365,7 +365,26 @@ func (u *SSHService) AnalysisLog(req dto.SearchForAnalysis) ([]dto.SSHLogAnalysi
} }
} }
return sortSlice, nil var backData dto.AnalysisRes
for _, item := range sortSlice {
backData.FailedCount += item.FailedCount
backData.SuccessfulCount += item.SuccessfulCount
}
var data []dto.SSHLogAnalysis
total, start, end := len(sortSlice), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
data = make([]dto.SSHLogAnalysis, 0)
} else {
if end >= total {
end = total
}
data = sortSlice[start:end]
}
backData.Items = data
backData.Total = int64(total)
return &backData, nil
} }
func (u *SSHService) LoadSSHConf() (string, error) { func (u *SSHService) LoadSSHConf() (string, error) {

View File

@ -1,5 +1,5 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT // Code generated by swaggo/swag. DO NOT EDIT.
// This file was generated by swaggo/swag
package docs package docs
import "github.com/swaggo/swag" import "github.com/swaggo/swag"
@ -7950,7 +7950,7 @@ const docTemplate = `{
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "操作 Node 项目 modules", "description": "操作 Node 项目 modules",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -14917,7 +14917,9 @@ const docTemplate = `{
"dto.SearchForAnalysis": { "dto.SearchForAnalysis": {
"type": "object", "type": "object",
"required": [ "required": [
"orderBy" "orderBy",
"page",
"pageSize"
], ],
"properties": { "properties": {
"orderBy": { "orderBy": {
@ -14926,6 +14928,12 @@ const docTemplate = `{
"Success", "Success",
"Failed" "Failed"
] ]
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
} }
} }
}, },
@ -16730,24 +16738,6 @@ const docTemplate = `{
"properties": { "properties": {
"ID": { "ID": {
"type": "integer" "type": "integer"
},
"module": {
"type": "string"
},
"operate": {
"type": "string",
"enum": [
"install",
"uninstall",
"update"
]
},
"pkgManager": {
"type": "string",
"enum": [
"npm",
"yarn"
]
} }
} }
}, },

View File

@ -7943,7 +7943,7 @@
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "操作 Node 项目 modules", "description": "操作 Node 项目 modules",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -14910,7 +14910,9 @@
"dto.SearchForAnalysis": { "dto.SearchForAnalysis": {
"type": "object", "type": "object",
"required": [ "required": [
"orderBy" "orderBy",
"page",
"pageSize"
], ],
"properties": { "properties": {
"orderBy": { "orderBy": {
@ -14919,6 +14921,12 @@
"Success", "Success",
"Failed" "Failed"
] ]
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
} }
} }
}, },
@ -16723,24 +16731,6 @@
"properties": { "properties": {
"ID": { "ID": {
"type": "integer" "type": "integer"
},
"module": {
"type": "string"
},
"operate": {
"type": "string",
"enum": [
"install",
"uninstall",
"update"
]
},
"pkgManager": {
"type": "string",
"enum": [
"npm",
"yarn"
]
} }
} }
}, },

View File

@ -1931,8 +1931,14 @@ definitions:
- Success - Success
- Failed - Failed
type: string type: string
page:
type: integer
pageSize:
type: integer
required: required:
- orderBy - orderBy
- page
- pageSize
type: object type: object
dto.SearchForTree: dto.SearchForTree:
properties: properties:
@ -3135,19 +3141,6 @@ definitions:
properties: properties:
ID: ID:
type: integer type: integer
module:
type: string
operate:
enum:
- install
- uninstall
- update
type: string
pkgManager:
enum:
- npm
- yarn
type: string
required: required:
- ID - ID
type: object type: object
@ -9207,7 +9200,7 @@ paths:
post: post:
consumes: consumes:
- application/json - application/json
description: 操作 Node 项目 modules description: 操作 Node 项目 modules
parameters: parameters:
- description: request - description: request
in: body in: body

View File

@ -136,6 +136,15 @@ export namespace Host {
info: string; info: string;
status: string; status: string;
} }
export interface analysisSSHLog extends ReqPage {
orderBy: string;
}
export interface logAnalysisRes {
total: number;
items: Array<logAnalysis>;
successfulCount: number;
failedCount: number;
}
export interface sshLog { export interface sshLog {
logs: Array<sshHistory>; logs: Array<sshHistory>;
successfulCount: number; successfulCount: number;

View File

@ -123,6 +123,6 @@ export const loadSecret = (mode: string) => {
export const loadSSHLogs = (params: Host.searchSSHLog) => { export const loadSSHLogs = (params: Host.searchSSHLog) => {
return http.post<Host.sshLog>(`/hosts/ssh/log`, params); return http.post<Host.sshLog>(`/hosts/ssh/log`, params);
}; };
export const loadAnalysis = (orderBy: string) => { export const loadAnalysis = (params: Host.analysisSSHLog) => {
return http.post<Array<Host.logAnalysis>>(`/hosts/ssh/log/analysis`, { orderBy: orderBy }, TimeoutEnum.T_40S); return http.post<Host.logAnalysisRes>(`/hosts/ssh/log/analysis`, params, TimeoutEnum.T_40S);
}; };

View File

@ -32,14 +32,16 @@
<el-button type="primary" @click="onChangeStatus('drop', null)" :disabled="selects.length === 0"> <el-button type="primary" @click="onChangeStatus('drop', null)" :disabled="selects.length === 0">
{{ $t('firewall.deny') }} {{ $t('firewall.deny') }}
</el-button> </el-button>
<ComplexTable v-model:selects="selects" class="mt-5" :data="data" @header-click="changeSort"> <el-table v-model:selects="selects" class="mt-5" :data="data" @header-click="changeSort">
<el-table-column type="selection" fix :selectable="selectable" /> <el-table-column type="selection" fix :selectable="selectable" />
<el-table-column :label="$t('logs.loginIP')" prop="address" min-width="40" /> <el-table-column :label="$t('logs.loginIP')" prop="address" min-width="40" />
<el-table-column :label="$t('ssh.belong')" prop="area" min-width="40" /> <el-table-column :label="$t('ssh.belong')" prop="area" min-width="40" />
<el-table-column prop="successfulCount" min-width="20"> <el-table-column prop="successfulCount" min-width="20">
<template #header> <template #header>
{{ $t('ssh.successful') }} {{ $t('ssh.successful') }}
<el-icon style="cursor: pointer" @click="search('Success')"><CaretBottom /></el-icon> <el-icon style="cursor: pointer" @click="(orderBy = 'Success') && search()">
<CaretBottom />
</el-icon>
</template> </template>
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link>{{ row.successfulCount }}</el-button> <el-button type="primary" link>{{ row.successfulCount }}</el-button>
@ -48,7 +50,9 @@
<el-table-column prop="failedCount" min-width="20"> <el-table-column prop="failedCount" min-width="20">
<template #header> <template #header>
{{ $t('ssh.failed') }} {{ $t('ssh.failed') }}
<el-icon style="cursor: pointer" @click="search('Failed')"><CaretBottom /></el-icon> <el-icon style="cursor: pointer" @click="(orderBy = 'Failed') && search()">
<CaretBottom />
</el-icon>
</template> </template>
<template #default="{ row }"> <template #default="{ row }">
<el-button type="danger" link>{{ row.failedCount }}</el-button> <el-button type="danger" link>{{ row.failedCount }}</el-button>
@ -76,7 +80,15 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</ComplexTable> </el-table>
<fu-table-pagination
class="float-right mt-4"
v-model:current-page="paginationConfig.currentPage"
v-model:page-size="paginationConfig.pageSize"
v-bind="paginationConfig"
@change="search()"
:layout="'prev, pager, next'"
/>
</div> </div>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
@ -120,7 +132,7 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { reactive, ref } from 'vue';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { loadAnalysis, operateIPRule } from '@/api/modules/host'; import { loadAnalysis, operateIPRule } from '@/api/modules/host';
import { MsgError, MsgSuccess } from '@/utils/message'; import { MsgError, MsgSuccess } from '@/utils/message';
@ -133,29 +145,38 @@ const data = ref();
const successfulTotalCount = ref(); const successfulTotalCount = ref();
const failedTotalCount = ref(); const failedTotalCount = ref();
const selects = ref<any>([]); const selects = ref<any>([]);
const orderBy = ref<string>('Failed');
const dialogVisible = ref(); const dialogVisible = ref();
const msg = ref(); const msg = ref();
const operation = ref(); const operation = ref();
const operationList = ref(); const operationList = ref();
const paginationConfig = reactive({
currentPage: 1,
pageSize: 10,
total: 0,
});
const acceptParams = (): void => { const acceptParams = (): void => {
search('Failed'); search();
drawerVisible.value = true; drawerVisible.value = true;
}; };
const search = async (status: string) => { const search = async () => {
let params = {
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
orderBy: orderBy.value,
};
loading.value = true; loading.value = true;
loadAnalysis(status) loadAnalysis(params)
.then((res) => { .then((res) => {
loading.value = false; loading.value = false;
data.value = res.data || []; data.value = res.data.items || [];
successfulTotalCount.value = 0; successfulTotalCount.value = res.data.successfulCount;
failedTotalCount.value = 0; failedTotalCount.value = res.data.failedCount;
for (const item of data.value) { paginationConfig.total = res.data.total;
successfulTotalCount.value += item.successfulCount;
failedTotalCount.value += item.failedCount;
}
}) })
.catch(() => { .catch(() => {
loading.value = false; loading.value = false;
@ -169,10 +190,12 @@ function selectable(row: any): boolean {
const changeSort = (column: any) => { const changeSort = (column: any) => {
switch (column.property) { switch (column.property) {
case 'successfulCount': case 'successfulCount':
search('Success'); orderBy.value = 'Success';
search();
return; return;
case 'failedCount': case 'failedCount':
search('Failed'); orderBy.value = 'Failed';
search();
return; return;
} }
}; };
@ -218,7 +241,7 @@ const submitOperation = async () => {
.then(() => { .then(() => {
loading.value = false; loading.value = false;
dialogVisible.value = false; dialogVisible.value = false;
search('Failed'); search();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
}) })
.finally(() => { .finally(() => {