1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 16:29:17 +08:00

feat: 实现防火墙批量删除及修改功能

This commit is contained in:
ssongliu 2023-03-28 15:17:13 +08:00 committed by ssongliu
parent c9d36d84f7
commit db74d010d7
14 changed files with 461 additions and 86 deletions

View File

@ -54,7 +54,7 @@ func (b *BaseApi) OperatePortRule(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
if err := firewallService.OperatePortRule(req); err != nil { if err := firewallService.OperatePortRule(req, true); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
@ -80,7 +80,82 @@ func (b *BaseApi) OperateIPRule(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
if err := firewallService.OperateAddressRule(req); err != nil { if err := firewallService.OperateAddressRule(req, true); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Firewall
// @Summary Create group
// @Description 批量删除防火墙规则
// @Accept json
// @Param request body dto.BatchRuleOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/firewall/ip [post]
func (b *BaseApi) BatchOperateRule(c *gin.Context) {
var req dto.BatchRuleOperate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := firewallService.BacthOperateRule(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Firewall
// @Summary Create group
// @Description 更新端口防火墙规则
// @Accept json
// @Param request body dto.PortRuleUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/firewall/update/port [post]
func (b *BaseApi) UpdatePortRule(c *gin.Context) {
var req dto.PortRuleUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := firewallService.UpdatePortRule(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Firewall
// @Summary Create group
// @Description 更新 ip 防火墙规则
// @Accept json
// @Param request body dto.AddrRuleUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/firewall/update/ip [post]
func (b *BaseApi) UpdateAddrRule(c *gin.Context) {
var req dto.AddrRuleUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := firewallService.UpdateAddrRule(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }

View File

@ -2,6 +2,7 @@ package dto
type RuleSearch struct { type RuleSearch struct {
PageInfo PageInfo
Info string `json:"info"`
Type string `json:"type" validate:"required"` Type string `json:"type" validate:"required"`
} }
@ -18,3 +19,18 @@ type AddrRuleOperate struct {
Address string `json:"address" validate:"required"` Address string `json:"address" validate:"required"`
Strategy string `json:"strategy" validate:"required,oneof=accept drop"` Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
} }
type PortRuleUpdate struct {
OldRule PortRuleOperate `json:"oldRule"`
NewRule PortRuleOperate `json:"newRule"`
}
type AddrRuleUpdate struct {
OldRule AddrRuleOperate `json:"oldRule"`
NewRule AddrRuleOperate `json:"newRule"`
}
type BatchRuleOperate struct {
Type string `json:"type" validate:"required"`
Rules []PortRuleOperate `json:"rules"`
}

View File

@ -1,6 +1,8 @@
package service package service
import ( import (
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/utils/firewall" "github.com/1Panel-dev/1Panel/backend/utils/firewall"
fireClient "github.com/1Panel-dev/1Panel/backend/utils/firewall/client" fireClient "github.com/1Panel-dev/1Panel/backend/utils/firewall/client"
@ -11,8 +13,11 @@ type FirewallService struct{}
type IFirewallService interface { type IFirewallService interface {
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error) SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
OperatePortRule(req dto.PortRuleOperate) error OperatePortRule(req dto.PortRuleOperate, reload bool) error
OperateAddressRule(req dto.AddrRuleOperate) error OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
UpdatePortRule(req dto.PortRuleUpdate) error
UpdateAddrRule(req dto.AddrRuleUpdate) error
BacthOperateRule(req dto.BatchRuleOperate) error
} }
func NewIFirewallService() IFirewallService { func NewIFirewallService() IFirewallService {
@ -33,13 +38,29 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
datas = ports if len(req.Info) != 0 {
for _, port := range ports {
if strings.Contains(port.Port, req.Info) {
datas = append(datas, port)
}
}
} else {
datas = ports
}
} else { } else {
address, err := client.ListAddress() addrs, err := client.ListAddress()
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
datas = address if len(req.Info) != 0 {
for _, addr := range addrs {
if strings.Contains(addr.Address, req.Info) {
datas = append(datas, addr)
}
}
} else {
datas = addrs
}
} }
total, start, end := len(datas), (req.Page-1)*req.PageSize, req.Page*req.PageSize total, start, end := len(datas), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total { if start > total {
@ -54,25 +75,36 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
return int64(total), backDatas, nil return int64(total), backDatas, nil
} }
func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate) error { func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {
client, err := firewall.NewFirewallClient() client, err := firewall.NewFirewallClient()
if err != nil { if err != nil {
return err return err
} }
if client.Name() == "ufw" {
req.Port = strings.ReplaceAll(req.Port, "-", ":")
if req.Operation == "remove" && req.Protocol == "tcp/udp" {
req.Protocol = ""
return u.operatePort(client, req)
}
}
if req.Protocol == "tcp/udp" { if req.Protocol == "tcp/udp" {
req.Protocol = "tcp" req.Protocol = "tcp"
if err := u.createPort(client, req); err != nil { if err := u.operatePort(client, req); err != nil {
return err return err
} }
req.Protocol = "udp" req.Protocol = "udp"
} }
if err := u.createPort(client, req); err != nil { if err := u.operatePort(client, req); err != nil {
return err return err
} }
return client.Reload() if reload {
return client.Reload()
}
return nil
} }
func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate) error { func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload bool) error {
client, err := firewall.NewFirewallClient() client, err := firewall.NewFirewallClient()
if err != nil { if err != nil {
return err return err
@ -82,18 +114,86 @@ func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate) error {
if err := copier.Copy(&fireInfo, &req); err != nil { if err := copier.Copy(&fireInfo, &req); err != nil {
return err return err
} }
if err := client.RichRules(fireInfo, req.Operation); err != nil {
addressList := strings.Split(req.Address, ",")
for _, addr := range addressList {
if len(addr) == 0 {
continue
}
fireInfo.Address = addr
if err := client.RichRules(fireInfo, req.Operation); err != nil {
return err
}
}
if reload {
return client.Reload()
}
return nil
}
func (u *FirewallService) UpdatePortRule(req dto.PortRuleUpdate) error {
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
if err := u.OperatePortRule(req.OldRule, false); err != nil {
return err
}
if err := u.OperatePortRule(req.NewRule, false); err != nil {
return err return err
} }
return client.Reload() return client.Reload()
} }
func (u *FirewallService) createPort(client firewall.FirewallClient, req dto.PortRuleOperate) error { func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
if err := u.OperateAddressRule(req.OldRule, false); err != nil {
return err
}
if err := u.OperateAddressRule(req.NewRule, false); err != nil {
return err
}
return client.Reload()
}
func (u *FirewallService) BacthOperateRule(req dto.BatchRuleOperate) error {
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
if req.Type == "port" {
for _, rule := range req.Rules {
if err := u.OperatePortRule(rule, false); err != nil {
return err
}
}
return client.Reload()
}
for _, rule := range req.Rules {
itemRule := dto.AddrRuleOperate{Operation: rule.Operation, Address: rule.Address, Strategy: rule.Strategy}
if err := u.OperateAddressRule(itemRule, false); err != nil {
return err
}
}
return client.Reload()
}
func (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.PortRuleOperate) error {
var fireInfo fireClient.FireInfo var fireInfo fireClient.FireInfo
if err := copier.Copy(&fireInfo, &req); err != nil { if err := copier.Copy(&fireInfo, &req); err != nil {
return err return err
} }
if client.Name() == "ufw" {
if len(fireInfo.Address) != 0 && fireInfo.Address != "Anywhere" {
return client.RichRules(fireInfo, req.Operation)
}
return client.Port(fireInfo, req.Operation)
}
if len(fireInfo.Address) != 0 || fireInfo.Strategy == "drop" { if len(fireInfo.Address) != 0 || fireInfo.Strategy == "drop" {
return client.RichRules(fireInfo, req.Operation) return client.RichRules(fireInfo, req.Operation)
} }

View File

@ -29,6 +29,9 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/firewall/search", baseApi.SearchFirewallRule) hostRouter.POST("/firewall/search", baseApi.SearchFirewallRule)
hostRouter.POST("/firewall/port", baseApi.OperatePortRule) hostRouter.POST("/firewall/port", baseApi.OperatePortRule)
hostRouter.POST("/firewall/ip", baseApi.OperateIPRule) hostRouter.POST("/firewall/ip", baseApi.OperateIPRule)
hostRouter.POST("/firewall/batch", baseApi.BatchOperateRule)
hostRouter.POST("/firewall/update/port", baseApi.UpdatePortRule)
hostRouter.POST("/firewall/update/addr", baseApi.UpdateAddrRule)
hostRouter.GET("/command", baseApi.ListCommand) hostRouter.GET("/command", baseApi.ListCommand)
hostRouter.POST("/command", baseApi.CreateCommand) hostRouter.POST("/command", baseApi.CreateCommand)

View File

@ -5,6 +5,7 @@ import (
) )
type FirewallClient interface { type FirewallClient interface {
Name() string
Start() error Start() error
Stop() error Stop() error
Reload() error Reload() error

View File

@ -21,6 +21,10 @@ func NewFirewalld() (*Firewall, error) {
return &Firewall{Client: ConnInfo}, nil return &Firewall{Client: ConnInfo}, nil
} }
func (f *Firewall) Name() string {
return "firewalld"
}
func (f *Firewall) Status() (string, error) { func (f *Firewall) Status() (string, error) {
stdout, err := f.Client.Run("firewall-cmd --state") stdout, err := f.Client.Run("firewall-cmd --state")
if err != nil { if err != nil {
@ -80,7 +84,7 @@ func (f *Firewall) ListPort() ([]FireInfo, error) {
continue continue
} }
itemRule := f.loadInfo(rule) itemRule := f.loadInfo(rule)
if len(itemRule.Port) != 0 { if len(itemRule.Port) != 0 && itemRule.Family == "ipv4" {
datas = append(datas, itemRule) datas = append(datas, itemRule)
} }
} }

View File

@ -21,6 +21,10 @@ func NewUfw() (*Ufw, error) {
return &Ufw{Client: ConnInfo}, nil return &Ufw{Client: ConnInfo}, nil
} }
func (f *Ufw) Name() string {
return "ufw"
}
func (f *Ufw) Status() (string, error) { func (f *Ufw) Status() (string, error) {
stdout, err := f.Client.Run("sudo ufw status") stdout, err := f.Client.Run("sudo ufw status")
if err != nil { if err != nil {
@ -49,10 +53,6 @@ func (f *Ufw) Stop() error {
} }
func (f *Ufw) Reload() error { func (f *Ufw) Reload() error {
stdout, err := f.Client.Run("sudo ufw reload")
if err != nil {
return fmt.Errorf("reload firewall failed, err: %s", stdout)
}
return nil return nil
} }
@ -73,7 +73,8 @@ func (f *Ufw) ListPort() ([]FireInfo, error) {
continue continue
} }
itemFire := f.loadInfo(line, "port") itemFire := f.loadInfo(line, "port")
if len(itemFire.Port) != 0 { if len(itemFire.Port) != 0 && itemFire.Port != "Anywhere" && !strings.Contains(itemFire.Port, ".") {
itemFire.Port = strings.ReplaceAll(itemFire.Port, ":", "-")
datas = append(datas, itemFire) datas = append(datas, itemFire)
} }
} }
@ -100,7 +101,11 @@ func (f *Ufw) ListAddress() ([]FireInfo, error) {
continue continue
} }
itemFire := f.loadInfo(line, "address") itemFire := f.loadInfo(line, "address")
if len(itemFire.Port) == 0 { if strings.Contains(itemFire.Port, ".") {
itemFire.Address += ("-" + itemFire.Port)
itemFire.Port = ""
}
if len(itemFire.Port) == 0 && len(itemFire.Address) != 0 {
datas = append(datas, itemFire) datas = append(datas, itemFire)
} }
} }
@ -108,16 +113,19 @@ func (f *Ufw) ListAddress() ([]FireInfo, error) {
} }
func (f *Ufw) Port(port FireInfo, operation string) error { func (f *Ufw) Port(port FireInfo, operation string) error {
switch operation { switch port.Strategy {
case "add": case "accept":
operation = "allow" port.Strategy = "allow"
case "remove": case "drop":
operation = "deny" port.Strategy = "deny"
default: default:
return fmt.Errorf("unsupport operation %s", operation) return fmt.Errorf("unsupport strategy %s", port.Strategy)
} }
command := fmt.Sprintf("sudo ufw %s %s", operation, port.Port) command := fmt.Sprintf("sudo ufw %s %s", port.Strategy, port.Port)
if operation == "remove" {
command = fmt.Sprintf("sudo ufw delete %s %s", port.Strategy, port.Port)
}
if len(port.Protocol) != 0 { if len(port.Protocol) != 0 {
command += fmt.Sprintf("/%s", port.Protocol) command += fmt.Sprintf("/%s", port.Protocol)
} }
@ -129,11 +137,25 @@ func (f *Ufw) Port(port FireInfo, operation string) error {
} }
func (f *Ufw) RichRules(rule FireInfo, operation string) error { func (f *Ufw) RichRules(rule FireInfo, operation string) error {
ruleStr := "sudo ufw " switch rule.Strategy {
case "accept":
rule.Strategy = "allow"
case "drop":
rule.Strategy = "deny"
default:
return fmt.Errorf("unsupport strategy %s", rule.Strategy)
}
ruleStr := fmt.Sprintf("sudo ufw %s ", rule.Strategy)
if operation == "remove" {
ruleStr = fmt.Sprintf("sudo ufw delete %s ", rule.Strategy)
}
if len(rule.Protocol) != 0 { if len(rule.Protocol) != 0 {
ruleStr += fmt.Sprintf("proto %s ", rule.Protocol) ruleStr += fmt.Sprintf("proto %s ", rule.Protocol)
} }
if len(rule.Address) != 0 { if strings.Contains(rule.Address, "-") {
ruleStr += fmt.Sprintf("from %s to %s ", strings.Split(rule.Address, "-")[0], strings.Split(rule.Address, "-")[1])
} else {
ruleStr += fmt.Sprintf("from %s ", rule.Address) ruleStr += fmt.Sprintf("from %s ", rule.Address)
} }
if len(rule.Port) != 0 { if len(rule.Port) != 0 {
@ -169,9 +191,12 @@ func (f *Ufw) loadInfo(line string, fireType string) FireInfo {
if len(fields) < 4 { if len(fields) < 4 {
return itemInfo return itemInfo
} }
if fields[1] == "(v6)" {
return itemInfo
}
if fields[0] == "Anywhere" && fireType != "port" { if fields[0] == "Anywhere" && fireType != "port" {
itemInfo.Strategy = "drop" itemInfo.Strategy = "drop"
if fields[2] == "ALLOW" { if fields[1] == "ALLOW" {
itemInfo.Strategy = "accept" itemInfo.Strategy = "accept"
} }
itemInfo.Address = fields[3] itemInfo.Address = fields[3]
@ -184,27 +209,13 @@ func (f *Ufw) loadInfo(line string, fireType string) FireInfo {
itemInfo.Port = fields[0] itemInfo.Port = fields[0]
itemInfo.Protocol = "tcp/udp" itemInfo.Protocol = "tcp/udp"
} }
itemInfo.Family = "ipv4"
if fields[1] == "(v6)" { if fields[1] == "ALLOW" {
if len(fields) < 5 { itemInfo.Strategy = "accept"
return itemInfo
}
itemInfo.Family = "ipv6"
if fields[2] == "ALLOW" {
itemInfo.Strategy = "accept"
} else {
itemInfo.Strategy = "drop"
}
itemInfo.Address = fields[4]
} else { } else {
itemInfo.Family = "ipv4" itemInfo.Strategy = "drop"
if fields[1] == "ALLOW" {
itemInfo.Strategy = "accept"
} else {
itemInfo.Strategy = "drop"
}
itemInfo.Address = fields[3]
} }
itemInfo.Address = fields[3]
return itemInfo return itemInfo
} }

View File

@ -77,4 +77,16 @@ export namespace Host {
address: string; address: string;
strategy: string; strategy: string;
} }
export interface UpdatePortRule {
oldRule: RulePort;
newRule: RulePort;
}
export interface UpdateAddrRule {
oldRule: RuleIP;
newRule: RuleIP;
}
export interface BatchRule {
type: string;
rules: Array<RulePort>;
}
} }

View File

@ -81,3 +81,12 @@ export const operatePortRule = (params: Host.RulePort) => {
export const operateIPRule = (params: Host.RuleIP) => { export const operateIPRule = (params: Host.RuleIP) => {
return http.post<Host.RuleIP>(`/hosts/firewall/ip`, params); return http.post<Host.RuleIP>(`/hosts/firewall/ip`, params);
}; };
export const updatePortRule = (params: Host.UpdatePortRule) => {
return http.post(`/hosts/firewall/update/port`, params);
};
export const updateAddrRule = (params: Host.UpdateAddrRule) => {
return http.post(`/hosts/firewall/update/addr`, params);
};
export const batchOperateRule = (params: Host.BatchRule) => {
return http.post(`/hosts/firewall/batch`, params);
};

View File

@ -1184,12 +1184,16 @@ const message = {
firewall: '防火墙', firewall: '防火墙',
protocol: '协议', protocol: '协议',
port: '端口', port: '端口',
portHelper: '支持输入多个端口 80,81 或者范围端口 80-88',
strategy: '策略', strategy: '策略',
accept: '允许', accept: '允许',
drop: '拒绝', drop: '拒绝',
source: '来源', source: '来源',
anyWhere: '所有 IP', anyWhere: '所有 IP',
address: '指定 IP', address: '指定 IP',
addressHelper1: '支持输入多个 IP 172.16.10.11,172.16.10.99',
addressHelper2: '支持输入 IP 172.16.10.0/24',
addressHelper3: '支持输入 IP 范围 172.16.10.11-172.16.10.99',
allIP: '所有 IP', allIP: '所有 IP',
portRule: '端口规则', portRule: '端口规则',
ipRule: 'IP 规则', ipRule: 'IP 规则',

View File

@ -4,10 +4,27 @@
<LayoutContent v-loading="loading" :title="$t('firewall.firewall')"> <LayoutContent v-loading="loading" :title="$t('firewall.firewall')">
<template #toolbar> <template #toolbar>
<el-row> <el-row>
<el-col :span="20"> <el-col :span="16">
<el-button type="primary" @click="onOpenDialog('create')"> <el-button type="primary" @click="onOpenDialog('create')">
{{ $t('commons.button.create') }} {{ $t('firewall.ipRule') }} {{ $t('commons.button.create') }} {{ $t('firewall.ipRule') }}
</el-button> </el-button>
<el-button @click="onDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
clearable
@clear="search()"
suffix-icon="Search"
@keyup.enter="search()"
@blur="search()"
:placeholder="$t('commons.button.search')"
></el-input>
</div>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
@ -50,9 +67,10 @@
import ComplexTable from '@/components/complex-table/index.vue'; import ComplexTable from '@/components/complex-table/index.vue';
import OperatrDialog from '@/views/host/firewall/ip/operate/index.vue'; import OperatrDialog from '@/views/host/firewall/ip/operate/index.vue';
import FireRouter from '@/views/host/firewall/index.vue'; import FireRouter from '@/views/host/firewall/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import LayoutContent from '@/layout/layout-content.vue'; import LayoutContent from '@/layout/layout-content.vue';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { operateIPRule, searchFireRule } from '@/api/modules/host'; import { batchOperateRule, searchFireRule } from '@/api/modules/host';
import { Host } from '@/api/interface/host'; import { Host } from '@/api/interface/host';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import i18n from '@/lang'; import i18n from '@/lang';
@ -61,6 +79,7 @@ import { MsgSuccess } from '@/utils/message';
const loading = ref(); const loading = ref();
const activeTag = ref('address'); const activeTag = ref('address');
const selects = ref<any>([]); const selects = ref<any>([]);
const searchName = ref();
const data = ref(); const data = ref();
const paginationConfig = reactive({ const paginationConfig = reactive({
@ -102,19 +121,36 @@ const onOpenDialog = async (
dialogRef.value!.acceptParams(params); dialogRef.value!.acceptParams(params);
}; };
const onDelete = async (row: Host.RuleInfo | null) => { const onDelete = async (row: Host.RuleIP | null) => {
ElMessageBox.confirm(i18n.global.t('commons.msg.delete'), i18n.global.t('commons.msg.deleteTitle'), { ElMessageBox.confirm(i18n.global.t('commons.msg.delete'), i18n.global.t('commons.msg.deleteTitle'), {
confirmButtonText: i18n.global.t('commons.button.confirm'), confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'), cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'warning', type: 'warning',
}).then(async () => { }).then(async () => {
let params = { let rules = [];
operation: 'remove', if (row) {
address: row.address, rules.push({
strategy: row.strategy, operation: 'remove',
}; address: row.address,
port: '',
source: '',
protocol: '',
strategy: row.strategy,
});
} else {
for (const item of selects.value) {
rules.push({
operation: 'remove',
address: item.address,
port: '',
source: '',
protocol: '',
strategy: item.strategy,
});
}
}
loading.value = true; loading.value = true;
await operateIPRule(params) await batchOperateRule({ type: 'port', rules: rules })
.then(() => { .then(() => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@ -127,9 +163,15 @@ const onDelete = async (row: Host.RuleInfo | null) => {
}; };
const buttons = [ const buttons = [
{
label: i18n.global.t('commons.button.edit'),
click: (row: Host.RuleIP) => {
onOpenDialog('edit', row);
},
},
{ {
label: i18n.global.t('commons.button.delete'), label: i18n.global.t('commons.button.delete'),
click: (row: Host.RuleInfo) => { click: (row: Host.RuleIP) => {
onDelete(row); onDelete(row);
}, },
}, },

View File

@ -14,6 +14,9 @@
clearable clearable
v-model="dialogData.rowData!.address" v-model="dialogData.rowData!.address"
/> />
<span class="input-help">{{ $t('firewall.addressHelper1') }}</span>
<span class="input-help">{{ $t('firewall.addressHelper2') }}</span>
<span class="input-help">{{ $t('firewall.addressHelper3') }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('firewall.strategy')" prop="strategy"> <el-form-item :label="$t('firewall.strategy')" prop="strategy">
<el-radio-group v-model="dialogData.rowData!.strategy"> <el-radio-group v-model="dialogData.rowData!.strategy">
@ -44,13 +47,15 @@ import { ElForm } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { Host } from '@/api/interface/host'; import { Host } from '@/api/interface/host';
import { operateIPRule } from '@/api/modules/host'; import { operateIPRule, updateAddrRule } from '@/api/modules/host';
import { deepCopy } from '@/utils/util';
const loading = ref(); const loading = ref();
const oldRule = ref<Host.RuleIP>();
interface DialogProps { interface DialogProps {
title: string; title: string;
rowData?: Host.RulePort; rowData?: Host.RuleIP;
getTableList?: () => Promise<any>; getTableList?: () => Promise<any>;
} }
const title = ref<string>(''); const title = ref<string>('');
@ -60,6 +65,9 @@ const dialogData = ref<DialogProps>({
}); });
const acceptParams = (params: DialogProps): void => { const acceptParams = (params: DialogProps): void => {
dialogData.value = params; dialogData.value = params;
if (dialogData.value.title === 'edit') {
oldRule.value = deepCopy(params.rowData);
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title); title.value = i18n.global.t('commons.button.' + dialogData.value.title);
drawerVisiable.value = true; drawerVisiable.value = true;
}; };
@ -83,11 +91,31 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
loading.value = true; loading.value = true;
dialogData.value.rowData.operation = 'add'; dialogData.value.rowData.operation = 'add';
if (!dialogData.value.rowData) return; if (!dialogData.value.rowData) return;
await operateIPRule(dialogData.value.rowData); if (dialogData.value.title === 'create') {
loading.value = false; await operateIPRule(dialogData.value.rowData)
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); .then(() => {
emit('search'); loading.value = false;
drawerVisiable.value = false; MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisiable.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
oldRule.value.operation = 'remove';
dialogData.value.rowData.operation = 'add';
await updateAddrRule({ oldRule: oldRule.value, newRule: dialogData.value.rowData })
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisiable.value = false;
})
.catch(() => {
loading.value = false;
});
}); });
}; };

View File

@ -4,10 +4,27 @@
<LayoutContent v-loading="loading" :title="$t('firewall.firewall')"> <LayoutContent v-loading="loading" :title="$t('firewall.firewall')">
<template #toolbar> <template #toolbar>
<el-row> <el-row>
<el-col :span="20"> <el-col :span="16">
<el-button type="primary" @click="onOpenDialog('create')"> <el-button type="primary" @click="onOpenDialog('create')">
{{ $t('commons.button.create') }}{{ $t('firewall.portRule') }} {{ $t('commons.button.create') }}{{ $t('firewall.portRule') }}
</el-button> </el-button>
<el-button @click="onDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
clearable
@clear="search()"
suffix-icon="Search"
@keyup.enter="search()"
@blur="search()"
:placeholder="$t('commons.button.search')"
></el-input>
</div>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
@ -51,10 +68,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue'; import ComplexTable from '@/components/complex-table/index.vue';
import FireRouter from '@/views/host/firewall/index.vue'; import FireRouter from '@/views/host/firewall/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import OperatrDialog from '@/views/host/firewall/port/operate/index.vue'; import OperatrDialog from '@/views/host/firewall/port/operate/index.vue';
import LayoutContent from '@/layout/layout-content.vue'; import LayoutContent from '@/layout/layout-content.vue';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { operatePortRule, searchFireRule } from '@/api/modules/host'; import { batchOperateRule, searchFireRule } from '@/api/modules/host';
import { Host } from '@/api/interface/host'; import { Host } from '@/api/interface/host';
import i18n from '@/lang'; import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
@ -63,6 +81,7 @@ import { ElMessageBox } from 'element-plus';
const loading = ref(); const loading = ref();
const activeTag = ref('port'); const activeTag = ref('port');
const selects = ref<any>([]); const selects = ref<any>([]);
const searchName = ref();
const data = ref(); const data = ref();
const paginationConfig = reactive({ const paginationConfig = reactive({
@ -74,7 +93,7 @@ const paginationConfig = reactive({
const search = async () => { const search = async () => {
let params = { let params = {
type: activeTag.value, type: activeTag.value,
info: '', info: searchName.value,
page: paginationConfig.currentPage, page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize, pageSize: paginationConfig.pageSize,
}; };
@ -112,16 +131,30 @@ const onDelete = async (row: Host.RuleInfo | null) => {
cancelButtonText: i18n.global.t('commons.button.cancel'), cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'warning', type: 'warning',
}).then(async () => { }).then(async () => {
let params = { let rules = [];
operation: 'remove', if (row) {
address: row.address, rules.push({
port: row.port, operation: 'remove',
source: '', address: row.address,
protocol: row.protocol, port: row.port,
strategy: row.strategy, source: '',
}; protocol: row.protocol,
strategy: row.strategy,
});
} else {
for (const item of selects.value) {
rules.push({
operation: 'remove',
address: item.address,
port: item.port,
source: '',
protocol: item.protocol,
strategy: item.strategy,
});
}
}
loading.value = true; loading.value = true;
await operatePortRule(params) await batchOperateRule({ type: 'port', rules: rules })
.then(() => { .then(() => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@ -134,6 +167,12 @@ const onDelete = async (row: Host.RuleInfo | null) => {
}; };
const buttons = [ const buttons = [
{
label: i18n.global.t('commons.button.edit'),
click: (row: Host.RulePort) => {
onOpenDialog('edit', row);
},
},
{ {
label: i18n.global.t('commons.button.delete'), label: i18n.global.t('commons.button.delete'),
click: (row: Host.RuleInfo) => { click: (row: Host.RuleInfo) => {

View File

@ -17,6 +17,7 @@
<el-form-item :label="$t('firewall.port')" prop="port"> <el-form-item :label="$t('firewall.port')" prop="port">
<el-input clearable v-model.trim="dialogData.rowData!.port" /> <el-input clearable v-model.trim="dialogData.rowData!.port" />
<span class="input-help">{{ $t('firewall.portHelper') }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('firewall.source')" prop="source"> <el-form-item :label="$t('firewall.source')" prop="source">
@ -63,9 +64,11 @@ import { ElForm } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { Host } from '@/api/interface/host'; import { Host } from '@/api/interface/host';
import { operatePortRule } from '@/api/modules/host'; import { operatePortRule, updatePortRule } from '@/api/modules/host';
import { deepCopy } from '@/utils/util';
const loading = ref(); const loading = ref();
const oldRule = ref<Host.RulePort>();
interface DialogProps { interface DialogProps {
title: string; title: string;
@ -79,6 +82,14 @@ const dialogData = ref<DialogProps>({
}); });
const acceptParams = (params: DialogProps): void => { const acceptParams = (params: DialogProps): void => {
dialogData.value = params; dialogData.value = params;
if (dialogData.value.title === 'edit') {
if (params.rowData.address && params.rowData.address !== 'Anywhere') {
dialogData.value.rowData.source = 'address';
} else {
dialogData.value.rowData.source = 'anyWhere';
}
oldRule.value = deepCopy(params.rowData);
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title); title.value = i18n.global.t('commons.button.' + dialogData.value.title);
drawerVisiable.value = true; drawerVisiable.value = true;
}; };
@ -101,14 +112,34 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
loading.value = true;
dialogData.value.rowData.operation = 'add'; dialogData.value.rowData.operation = 'add';
if (!dialogData.value.rowData) return; if (!dialogData.value.rowData) return;
await operatePortRule(dialogData.value.rowData); loading.value = true;
loading.value = false; if (dialogData.value.title === 'create') {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); await operatePortRule(dialogData.value.rowData)
emit('search'); .then(() => {
drawerVisiable.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisiable.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
oldRule.value.operation = 'remove';
dialogData.value.rowData.operation = 'add';
await updatePortRule({ oldRule: oldRule.value, newRule: dialogData.value.rowData })
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisiable.value = false;
})
.catch(() => {
loading.value = false;
});
}); });
}; };