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:
parent
faf4174adf
commit
c01b3764f8
@ -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"`
|
||||||
|
@ -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) {
|
||||||
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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(() => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user