Browse Source

feat: 优化 SSH 日志获取 (#3348)

Refs #3337
pull/3363/head
ssongliu 11 months ago committed by GitHub
parent
commit
bcd88c6eca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      backend/app/api/v1/ssh.go
  2. 20
      backend/app/dto/ssh.go
  3. 261
      backend/app/service/ssh.go
  4. 1
      backend/router/ro_host.go
  5. 82
      cmd/server/docs/docs.go
  6. 82
      cmd/server/docs/swagger.json
  7. 53
      cmd/server/docs/swagger.yaml
  8. 3
      frontend/src/api/modules/host.ts
  9. 259
      frontend/src/views/host/ssh/log/analysis/index.vue

22
backend/app/api/v1/ssh.go

@ -131,28 +131,6 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Tags SSH
// @Summary Analysis host SSH logs
// @Description 分析 SSH 登录日志
// @Accept json
// @Param request body dto.SearchForAnalysis true "request"
// @Success 200 {array} dto.SSHLogAnalysis
// @Security ApiKeyAuth
// @Router /host/ssh/log/analysis [post]
func (b *BaseApi) AnalysisLog(c *gin.Context) {
var req dto.SearchForAnalysis
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := sshService.AnalysisLog(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags SSH
// @Summary Load host SSH logs
// @Description 获取 SSH 登录日志

20
backend/app/dto/ssh.go

@ -44,26 +44,6 @@ type SSHLog struct {
FailedCount int `json:"failedCount"`
}
type SearchForAnalysis struct {
PageInfo
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 {
Address string `json:"address"`
Area string `json:"area"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
Status string `json:"status"`
}
type SSHHistory struct {
Date time.Time `json:"date"`
DateStr string `json:"dateStr"`

261
backend/app/service/ssh.go

@ -32,7 +32,6 @@ type ISSHService interface {
UpdateByFile(value string) error
Update(req dto.SSHUpdate) error
GenerateSSH(req dto.GenerateSSH) error
AnalysisLog(req dto.SearchForAnalysis) (*dto.AnalysisRes, error)
LoadSSHSecret(mode string) (string, error)
LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error)
@ -310,9 +309,9 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
case constant.StatusSuccess:
commandItem = fmt.Sprintf("cat %s | grep -a Accepted %s", file.Name, command)
case constant.StatusFailed:
commandItem = fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", file.Name, command)
commandItem = fmt.Sprintf("cat %s | grep -a 'Failed password for' %s", file.Name, command)
default:
commandItem = fmt.Sprintf("cat %s | grep -aE '(Failed password for|Accepted)' | grep -v 'invalid' %s", file.Name, command)
commandItem = fmt.Sprintf("cat %s | grep -aE '(Failed password for|Accepted)' %s", file.Name, command)
}
}
if strings.HasPrefix(path.Base(file.Name), "auth.log") {
@ -320,9 +319,9 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
case constant.StatusSuccess:
commandItem = fmt.Sprintf("cat %s | grep -a Accepted %s", file.Name, command)
case constant.StatusFailed:
commandItem = fmt.Sprintf("cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' | grep -v 'invalid' %s", file.Name, command)
commandItem = fmt.Sprintf("cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' %s", file.Name, command)
default:
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Failed password for|Connection closed by authenticating user|Accepted)\" | grep -v 'invalid' %s", file.Name, command)
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Failed password for|Connection closed by authenticating user|Accepted)\" %s", file.Name, command)
}
}
dataItem, successCount, failedCount := loadSSHData(commandItem, showCountFrom, showCountTo, file.Year, qqWry, nyc)
@ -337,89 +336,6 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
return &data, nil
}
func (u *SSHService) AnalysisLog(req dto.SearchForAnalysis) (*dto.AnalysisRes, error) {
var fileList []string
baseDir := "/var/log"
if err := filepath.Walk(baseDir, func(pathItem string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && (strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth")) {
if !strings.HasSuffix(info.Name(), ".gz") {
fileList = append(fileList, pathItem)
return nil
}
itemFileName := strings.TrimSuffix(pathItem, ".gz")
if _, err := os.Stat(itemFileName); err != nil && os.IsNotExist(err) {
if err := handleGunzip(pathItem); err == nil {
fileList = append(fileList, itemFileName)
}
}
}
return nil
}); err != nil {
return nil, err
}
command := ""
sortMap := make(map[string]dto.SSHLogAnalysis)
for _, file := range fileList {
commandItem := ""
if strings.HasPrefix(path.Base(file), "secure") {
commandItem = fmt.Sprintf("cat %s | grep -aE '(Failed password for|Accepted)' | grep -v 'invalid' %s", file, command)
}
if strings.HasPrefix(path.Base(file), "auth.log") {
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Connection closed by authenticating user|Accepted)\" | grep -v 'invalid' %s", file, command)
}
loadSSHDataForAnalysis(sortMap, commandItem)
}
var sortSlice []dto.SSHLogAnalysis
for key, value := range sortMap {
sortSlice = append(sortSlice, dto.SSHLogAnalysis{Address: key, SuccessfulCount: value.SuccessfulCount, FailedCount: value.FailedCount, Status: "accept"})
}
if req.OrderBy == constant.StatusSuccess {
sort.Slice(sortSlice, func(i, j int) bool {
return sortSlice[i].SuccessfulCount > sortSlice[j].SuccessfulCount
})
} else {
sort.Slice(sortSlice, func(i, j int) bool {
return sortSlice[i].FailedCount > sortSlice[j].FailedCount
})
}
qqWry, _ := qqwry.NewQQwry()
rules, _ := listIpRules("drop")
for i := 0; i < len(sortSlice); i++ {
sortSlice[i].Area = qqWry.Find(sortSlice[i].Address).Area
for _, rule := range rules {
if sortSlice[i].Address == rule {
sortSlice[i].Status = "drop"
break
}
}
}
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) {
if _, err := os.Stat("/etc/ssh/sshd_config"); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
@ -529,147 +445,62 @@ func loadSSHData(command string, showCountFrom, showCountTo, currentYear int, qq
return datas, successCount, failedCount
}
func loadSSHDataForAnalysis(analysisMap map[string]dto.SSHLogAnalysis, commandItem string) {
stdout, err := cmd.Exec(commandItem)
if err != nil {
return
}
lines := strings.Split(string(stdout), "\n")
for i := len(lines) - 1; i >= 0; i-- {
var itemData dto.SSHHistory
switch {
case strings.Contains(lines[i], "Failed password for"):
itemData = loadFailedSecureDatas(lines[i])
case strings.Contains(lines[i], "Connection closed by authenticating user"):
itemData = loadFailedAuthDatas(lines[i])
case strings.Contains(lines[i], "Accepted "):
itemData = loadSuccessDatas(lines[i])
}
if len(itemData.Address) != 0 {
if val, ok := analysisMap[itemData.Address]; ok {
if itemData.Status == constant.StatusSuccess {
val.SuccessfulCount++
} else {
val.FailedCount++
}
analysisMap[itemData.Address] = val
} else {
item := dto.SSHLogAnalysis{
Address: itemData.Address,
SuccessfulCount: 0,
FailedCount: 0,
}
if itemData.Status == constant.StatusSuccess {
item.SuccessfulCount = 1
} else {
item.FailedCount = 1
}
analysisMap[itemData.Address] = item
}
}
}
}
func loadSuccessDatas(line string) dto.SSHHistory {
var data dto.SSHHistory
parts := strings.Fields(line)
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
if err != nil {
if len(parts) < 14 {
return data
}
data = dto.SSHHistory{
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
AuthMode: parts[6],
User: parts[8],
Address: parts[10],
Port: parts[12],
Status: constant.StatusSuccess,
}
} else {
if len(parts) < 12 {
return data
}
data = dto.SSHHistory{
DateStr: t.Format("2006 Jan 2 15:04:05"),
AuthMode: parts[4],
User: parts[6],
Address: parts[8],
Port: parts[10],
Status: constant.StatusSuccess,
}
}
index, dataStr := analyzeDateStr(parts)
if dataStr == "" {
return data
}
data.DateStr = dataStr
data.AuthMode = parts[4+index]
data.User = parts[6+index]
data.Address = parts[8+index]
data.Port = parts[10+index]
data.Status = constant.StatusSuccess
return data
}
func loadFailedAuthDatas(line string) dto.SSHHistory {
var data dto.SSHHistory
parts := strings.Fields(line)
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
if err != nil {
if len(parts) < 14 {
return data
}
data = dto.SSHHistory{
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
AuthMode: parts[8],
User: parts[10],
Address: parts[11],
Port: parts[13],
Status: constant.StatusFailed,
}
index, dataStr := analyzeDateStr(parts)
if dataStr == "" {
return data
}
data.DateStr = dataStr
if index == 2 {
data.User = parts[10]
} else {
if len(parts) < 12 {
return data
}
data = dto.SSHHistory{
DateStr: t.Format("2006 Jan 2 15:04:05"),
AuthMode: parts[6],
User: parts[7],
Address: parts[9],
Port: parts[11],
Status: constant.StatusFailed,
}
data.User = parts[7]
}
data.AuthMode = parts[6+index]
data.Address = parts[9+index]
data.Port = parts[11+index]
data.Status = constant.StatusFailed
if strings.Contains(line, ": ") {
data.Message = strings.Split(line, ": ")[1]
}
return data
}
func loadFailedSecureDatas(line string) dto.SSHHistory {
var data dto.SSHHistory
parts := strings.Fields(line)
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
if err != nil {
if len(parts) < 14 {
return data
}
data = dto.SSHHistory{
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
AuthMode: parts[6],
User: parts[8],
Address: parts[10],
Port: parts[12],
Status: constant.StatusFailed,
}
index, dataStr := analyzeDateStr(parts)
if dataStr == "" {
return data
}
data.DateStr = dataStr
if strings.Contains(line, " invalid ") {
data.AuthMode = parts[4+index]
index += 2
} else {
if len(parts) < 12 {
return data
}
index := 0
if strings.Contains(line, " invalid user") {
index = 2
}
data = dto.SSHHistory{
DateStr: t.Format("2006 Jan 2 15:04:05"),
AuthMode: parts[4],
User: parts[index+6],
Address: parts[index+8],
Port: parts[index+10],
Status: constant.StatusFailed,
}
data.AuthMode = parts[4+index]
}
data.User = parts[6+index]
data.Address = parts[8+index]
data.Port = parts[10+index]
data.Status = constant.StatusFailed
if strings.Contains(line, ": ") {
data.Message = strings.Split(line, ": ")[1]
}
@ -699,3 +530,17 @@ func loadDate(currentYear int, DateStr string, nyc *time.Location) time.Time {
}
return itemDate
}
func analyzeDateStr(parts []string) (int, string) {
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
if err != nil {
if len(parts) < 14 {
return 0, ""
}
return 2, fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2])
}
if len(parts) < 12 {
return 0, ""
}
return 0, t.Format("2006 Jan 2 15:04:05")
}

1
backend/router/ro_host.go

@ -41,7 +41,6 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/ssh/generate", baseApi.GenerateSSH)
hostRouter.POST("/ssh/secret", baseApi.LoadSSHSecret)
hostRouter.POST("/ssh/log", baseApi.LoadSSHLogs)
hostRouter.POST("/ssh/log/analysis", baseApi.AnalysisLog)
hostRouter.POST("/ssh/conffile/update", baseApi.UpdateSSHByfile)
hostRouter.POST("/ssh/operate", baseApi.OperateSSH)

82
cmd/server/docs/docs.go

@ -6582,45 +6582,6 @@ const docTemplate = `{
}
}
},
"/host/ssh/log/analysis": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "分析 SSH 登录日志",
"consumes": [
"application/json"
],
"tags": [
"SSH"
],
"summary": "Analysis host SSH logs",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchForAnalysis"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.SSHLogAnalysis"
}
}
}
}
}
},
"/host/ssh/operate": {
"post": {
"security": [
@ -16568,26 +16529,6 @@ const docTemplate = `{
}
}
},
"dto.SSHLogAnalysis": {
"type": "object",
"properties": {
"address": {
"type": "string"
},
"area": {
"type": "string"
},
"failedCount": {
"type": "integer"
},
"status": {
"type": "string"
},
"successfulCount": {
"type": "integer"
}
}
},
"dto.SSHUpdate": {
"type": "object",
"required": [
@ -16642,29 +16583,6 @@ const docTemplate = `{
}
}
},
"dto.SearchForAnalysis": {
"type": "object",
"required": [
"orderBy",
"page",
"pageSize"
],
"properties": {
"orderBy": {
"type": "string",
"enum": [
"Success",
"Failed"
]
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"dto.SearchForTree": {
"type": "object",
"properties": {

82
cmd/server/docs/swagger.json

@ -6575,45 +6575,6 @@
}
}
},
"/host/ssh/log/analysis": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "分析 SSH 登录日志",
"consumes": [
"application/json"
],
"tags": [
"SSH"
],
"summary": "Analysis host SSH logs",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchForAnalysis"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.SSHLogAnalysis"
}
}
}
}
}
},
"/host/ssh/operate": {
"post": {
"security": [
@ -16561,26 +16522,6 @@
}
}
},
"dto.SSHLogAnalysis": {
"type": "object",
"properties": {
"address": {
"type": "string"
},
"area": {
"type": "string"
},
"failedCount": {
"type": "integer"
},
"status": {
"type": "string"
},
"successfulCount": {
"type": "integer"
}
}
},
"dto.SSHUpdate": {
"type": "object",
"required": [
@ -16635,29 +16576,6 @@
}
}
},
"dto.SearchForAnalysis": {
"type": "object",
"required": [
"orderBy",
"page",
"pageSize"
],
"properties": {
"orderBy": {
"type": "string",
"enum": [
"Success",
"Failed"
]
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"dto.SearchForTree": {
"type": "object",
"properties": {

53
cmd/server/docs/swagger.yaml

@ -2150,19 +2150,6 @@ definitions:
totalCount:
type: integer
type: object
dto.SSHLogAnalysis:
properties:
address:
type: string
area:
type: string
failedCount:
type: integer
status:
type: string
successfulCount:
type: integer
type: object
dto.SSHUpdate:
properties:
key:
@ -2200,22 +2187,6 @@ definitions:
- ssl
- sslType
type: object
dto.SearchForAnalysis:
properties:
orderBy:
enum:
- Success
- Failed
type: string
page:
type: integer
pageSize:
type: integer
required:
- orderBy
- page
- pageSize
type: object
dto.SearchForTree:
properties:
info:
@ -8966,30 +8937,6 @@ paths:
summary: Load host SSH logs
tags:
- SSH
/host/ssh/log/analysis:
post:
consumes:
- application/json
description: 分析 SSH 登录日志
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.SearchForAnalysis'
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/dto.SSHLogAnalysis'
type: array
security:
- ApiKeyAuth: []
summary: Analysis host SSH logs
tags:
- SSH
/host/ssh/operate:
post:
consumes:

3
frontend/src/api/modules/host.ts

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

259
frontend/src/views/host/ssh/log/analysis/index.vue

@ -1,259 +0,0 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="$t('menu.home')" :back="handleClose" />
</template>
<div v-loading="loading">
<el-form label-position="top" class="ml-5">
<el-row type="flex" justify="center" :gutter="20">
<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item>
<template #label>
<span class="status-label">{{ $t('ssh.successful') }}</span>
</template>
<span class="status-count">{{ successfulTotalCount }}</span>
</el-form-item>
</el-col>
<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item>
<template #label>
<span class="status-label">{{ $t('ssh.failed') }}</span>
</template>
<span class="status-count">{{ failedTotalCount }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-button type="primary" @click="onChangeStatus('accept', null)" :disabled="selects.length === 0">
{{ $t('firewall.allow') }}
</el-button>
<el-button type="primary" @click="onChangeStatus('drop', null)" :disabled="selects.length === 0">
{{ $t('firewall.deny') }}
</el-button>
<ComplexTable v-model:selects="selects" class="mt-5" :data="data" @header-click="changeSort">
<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('ssh.belong')" prop="area" min-width="40" />
<el-table-column prop="successfulCount" min-width="20">
<template #header>
{{ $t('ssh.successful') }}
<el-icon style="cursor: pointer" @click="(orderBy = 'Success') && search()">
<CaretBottom />
</el-icon>
</template>
<template #default="{ row }">
<el-button type="primary" link>{{ row.successfulCount }}</el-button>
</template>
</el-table-column>
<el-table-column prop="failedCount" min-width="20">
<template #header>
{{ $t('ssh.failed') }}
<el-icon style="cursor: pointer" @click="(orderBy = 'Failed') && search()">
<CaretBottom />
</el-icon>
</template>
<template #default="{ row }">
<el-button type="danger" link>{{ row.failedCount }}</el-button>
</template>
</el-table-column>
<el-table-column :min-width="30" :label="$t('commons.table.status')" prop="strategy">
<template #default="{ row }">
<el-button
v-if="row.status === 'accept'"
:disabled="!selectable(row)"
@click="onChangeStatus('drop', row)"
link
type="success"
>
{{ $t('commons.status.accept') }}
</el-button>
<el-button
v-else
link
:disabled="!selectable(row)"
type="danger"
@click="onChangeStatus('accept', row)"
>
{{ $t('commons.status.deny') }}
</el-button>
</template>
</el-table-column>
</ComplexTable>
<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>
<template #footer>
<span class="dialog-footer">
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-drawer>
<el-dialog
v-model="dialogVisible"
:title="$t('firewall.' + (operation === 'drop' ? 'deny' : 'allow'))"
width="30%"
:close-on-click-modal="false"
>
<el-row>
<el-col :span="20" :offset="2">
<el-alert :title="msg" show-icon type="error" :closable="false"></el-alert>
<div class="resource">
<ul v-for="(row, index) in operationList" :key="index">
<li>
<span>{{ row }}</span>
</li>
</ul>
</div>
</el-col>
</el-row>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false" :disabled="loading">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="submitOperation" v-loading="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { loadAnalysis, operateIPRule } from '@/api/modules/host';
import { MsgError, MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
import { Host } from '@/api/interface/host';
const drawerVisible = ref();
const loading = ref();
const data = ref();
const successfulTotalCount = ref();
const failedTotalCount = ref();
const selects = ref<any>([]);
const orderBy = ref<string>('Failed');
const dialogVisible = ref();
const msg = ref();
const operation = ref();
const operationList = ref();
const paginationConfig = reactive({
currentPage: 1,
pageSize: 10,
total: 0,
});
const acceptParams = (): void => {
search();
drawerVisible.value = true;
};
const search = async () => {
let params = {
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
orderBy: orderBy.value,
};
loading.value = true;
loadAnalysis(params)
.then((res) => {
loading.value = false;
data.value = res.data.items || [];
successfulTotalCount.value = res.data.successfulCount;
failedTotalCount.value = res.data.failedCount;
paginationConfig.total = res.data.total;
})
.catch(() => {
loading.value = false;
});
};
function selectable(row: any): boolean {
return row.address !== '127.0.0.1' && row.address !== '::1';
}
const changeSort = (column: any) => {
switch (column.property) {
case 'successfulCount':
orderBy.value = 'Success';
search();
return;
case 'failedCount':
orderBy.value = 'Failed';
search();
return;
}
};
const onChangeStatus = async (status: string, row: Host.logAnalysis | null) => {
operationList.value = [];
if (row) {
if (row.status !== status) {
operationList.value.push(row.address);
}
} else {
for (const item of selects.value) {
if (item.status !== status) {
operationList.value.push(item.address);
}
}
}
if (operationList.value.length === 0) {
MsgError(
i18n.global.t('ssh.noAddrWarning', [i18n.global.t('firewall.' + (status === 'drop' ? 'deny' : 'allow'))]),
);
return;
}
operation.value = status;
msg.value = status === 'drop' ? i18n.global.t('ssh.denyHelper') : i18n.global.t('ssh.acceptHelper');
dialogVisible.value = true;
};
const submitOperation = async () => {
loading.value = true;
operateIPRule({
operation: operation.value === 'drop' ? 'add' : 'remove',
address: operationList.value.join(','),
strategy: 'drop',
description: '',
})
.then(() => {
loading.value = false;
dialogVisible.value = false;
search();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.finally(() => {
loading.value = false;
});
};
const handleClose = () => {
drawerVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style scoped lang="scss">
.resource {
margin-top: 10px;
max-height: 400px;
overflow: auto;
}
</style>
Loading…
Cancel
Save