1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-14 01:34:47 +08:00

feat: 同步修改防火墙端口

This commit is contained in:
ssongliu 2023-04-01 00:51:25 +08:00 committed by ssongliu
parent 1c5d01b11c
commit 8902fdc78a
18 changed files with 363 additions and 121 deletions

View File

@ -112,6 +112,7 @@ type RootInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Port int64 `json:"port"`
HttpsPort int64 `json:"httpsPort"`
Password string `json:"password"`
UserPassword string `json:"userPassword"`
ContainerName string `json:"containerName"`
@ -152,6 +153,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
info.UserPassword = userPassword
}
info.Port = int64(appInstall.HttpPort)
info.HttpsPort = int64(appInstall.HttpsPort)
info.ID = appInstall.ID
info.ContainerName = appInstall.ContainerName
info.Name = appInstall.Name

View File

@ -387,6 +387,10 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
}
}
if err := OperateFirewallPort([]int{int(appInstall.Port)}, []int{int(req.Port)}); err != nil {
global.LOG.Errorf("allow firewall failed, err: %v", err)
}
return nil
}

View File

@ -2,9 +2,12 @@ package service
import (
"fmt"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/firewall"
fireClient "github.com/1Panel-dev/1Panel/backend/utils/firewall/client"
"github.com/jinzhu/copier"
@ -101,6 +104,22 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
backDatas = datas[start:end]
}
if req.Type == "port" {
apps := u.loadPortByApp()
for i := 0; i < len(backDatas); i++ {
backDatas[i].IsUsed = common.ScanPortWithProtocol(backDatas[i].Port, backDatas[i].Protocol)
if backDatas[i].Protocol == "udp" {
continue
}
for _, app := range apps {
if app.HttpPort == backDatas[i].Port || app.HttpsPort == backDatas[i].Port {
backDatas[i].APPName = app.AppName
break
}
}
}
}
return int64(total), backDatas, nil
}
@ -111,9 +130,24 @@ func (u *FirewallService) OperateFirewall(operation string) error {
}
switch operation {
case "start":
return client.Start()
if err := client.Start(); err != nil {
return err
}
serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
if err != nil {
return err
}
if err := client.Port(fireClient.FireInfo{Port: serverPort.Value, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
_, _ = cmd.Exec("systemctl restart docker")
return nil
case "stop":
return client.Stop()
if err := client.Stop(); err != nil {
return err
}
_, _ = cmd.Exec("systemctl restart docker")
return nil
case "disablePing":
return client.UpdatePingStatus("0")
case "enablePing":
@ -134,16 +168,47 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
return u.operatePort(client, req)
}
}
if req.Protocol == "tcp/udp" {
req.Protocol = "tcp"
if err := u.operatePort(client, req); err != nil {
return err
if client.Name() == "firewalld" && strings.Contains(req.Port, ",") {
ports := strings.Split(req.Port, ",")
for _, port := range ports {
if len(port) == 0 {
continue
}
req.Port = port
req.Protocol = "tcp"
if err := u.operatePort(client, req); err != nil {
return err
}
req.Protocol = "udp"
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
req.Protocol = "tcp"
if err := u.operatePort(client, req); err != nil {
return err
}
req.Protocol = "udp"
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
if strings.Contains(req.Port, ",") {
ports := strings.Split(req.Port, ",")
for _, port := range ports {
req.Port = port
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
if err := u.operatePort(client, req); err != nil {
return err
}
}
req.Protocol = "udp"
}
if err := u.operatePort(client, req); err != nil {
return err
}
if reload {
return client.Reload()
@ -228,6 +293,26 @@ func (u *FirewallService) BacthOperateRule(req dto.BatchRuleOperate) error {
return client.Reload()
}
func OperateFirewallPort(oldPorts, newPorts []int) error {
fmt.Printf("old: %v, new: %v \n", oldPorts, newPorts)
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
for _, port := range newPorts {
if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
}
for _, port := range oldPorts {
if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "remove"); err != nil {
return err
}
}
return client.Reload()
}
func (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.PortRuleOperate) error {
var fireInfo fireClient.FireInfo
if err := copier.Copy(&fireInfo, &req); err != nil {
@ -246,3 +331,31 @@ func (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.Po
}
return client.Port(fireInfo, req.Operation)
}
type portOfApp struct {
AppName string
HttpPort string
HttpsPort string
}
func (u *FirewallService) loadPortByApp() []portOfApp {
var datas []portOfApp
apps, err := appInstallRepo.ListBy()
if err != nil {
return datas
}
for i := 0; i < len(apps); i++ {
datas = append(datas, portOfApp{
AppName: apps[i].App.Key,
HttpPort: strconv.Itoa(apps[i].HttpPort),
HttpsPort: strconv.Itoa(apps[i].HttpsPort),
})
}
systemPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
if err != nil {
return datas
}
datas = append(datas, portOfApp{AppName: "1panel", HttpPort: systemPort.Value})
return datas
}

View File

@ -70,7 +70,14 @@ func (u *SettingService) UpdatePort(port uint) error {
if common.ScanPort(int(port)) {
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
}
serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
if err != nil {
return err
}
portValue, _ := strconv.Atoi(serverPort.Value)
if err := OperateFirewallPort([]int{portValue}, []int{int(port)}); err != nil {
global.LOG.Errorf("set system firewall ports failed, err: %v", err)
}
if err := settingRepo.Update("ServerPort", strconv.Itoa(int(port))); err != nil {
return err
}

View File

@ -11,6 +11,8 @@ import (
"sort"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
)
func CompareVersion(version1 string, version2 string) bool {
@ -92,6 +94,27 @@ func ScanPort(port int) bool {
return false
}
func ScanPortWithProtocol(port, Protocol string) bool {
command := "netstat -ntpl"
if Protocol == "udp" {
command = "netstat -nupl"
}
stdout, err := cmd.Execf("%s | awk '{print $4}' ", command)
if err != nil {
return false
}
lines := strings.Split(stdout, "\n")
if len(lines) == 0 {
return false
}
for _, line := range lines {
if strings.HasSuffix(line, ":"+port) {
return true
}
}
return false
}
func ExistWithStrArray(str string, arr []string) bool {
for _, a := range arr {
if strings.Contains(a, str) {

View File

@ -20,7 +20,10 @@ func (f *Firewall) Name() string {
func (f *Firewall) Status() (string, error) {
stdout, _ := cmd.Exec("firewall-cmd --state")
return strings.ReplaceAll(stdout, "\n", ""), nil
if stdout == "running\n" {
return "running", nil
}
return "not running", nil
}
func (f *Firewall) Version() (string, error) {
@ -83,6 +86,9 @@ func (f *Firewall) ListPort() ([]FireInfo, error) {
ports := strings.Split(strings.ReplaceAll(stdout, "\n", ""), " ")
var datas []FireInfo
for _, port := range ports {
if len(port) == 0 {
continue
}
var itemPort FireInfo
if strings.Contains(port, "/") {
itemPort.Port = strings.Split(port, "/")[0]
@ -121,7 +127,7 @@ func (f *Firewall) ListAddress() ([]FireInfo, error) {
continue
}
itemRule := f.loadInfo(rule)
if len(itemRule.Port) == 0 {
if len(itemRule.Port) == 0 && len(itemRule.Address) != 0 {
datas = append(datas, itemRule)
}
}
@ -137,18 +143,22 @@ func (f *Firewall) Port(port FireInfo, operation string) error {
}
func (f *Firewall) RichRules(rule FireInfo, operation string) error {
ruleStr := "rule family=ipv4 "
if len(rule.Address) != 0 {
ruleStr += fmt.Sprintf("source address=%s ", rule.Address)
ruleStr := ""
if strings.Contains(rule.Address, "-") {
ruleStr = fmt.Sprintf("rule source ipset=%s %s", rule.Address, rule.Strategy)
} else {
ruleStr = "rule family=ipv4 "
if len(rule.Address) != 0 {
ruleStr += fmt.Sprintf("source address=%s ", rule.Address)
}
if len(rule.Port) != 0 {
ruleStr += fmt.Sprintf("port port=%s ", rule.Port)
}
if len(rule.Protocol) != 0 {
ruleStr += fmt.Sprintf("protocol=%s ", rule.Protocol)
}
ruleStr += rule.Strategy
}
if len(rule.Port) != 0 {
ruleStr += fmt.Sprintf("port port=%s ", rule.Port)
}
if len(rule.Protocol) != 0 {
ruleStr += fmt.Sprintf("protocol=%s ", rule.Protocol)
}
ruleStr += rule.Strategy
stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, ruleStr)
if err != nil {
return fmt.Errorf("%s rich rules failed, err: %s", operation, stdout)
@ -176,13 +186,15 @@ func (f *Firewall) loadInfo(line string) FireInfo {
switch {
case strings.Contains(item, "family="):
itemRule.Family = strings.ReplaceAll(item, "family=", "")
case strings.Contains(item, "ipset="):
itemRule.Address = strings.ReplaceAll(item, "ipset=", "")
case strings.Contains(item, "address="):
itemRule.Address = strings.ReplaceAll(item, "address=", "")
case strings.Contains(item, "port="):
itemRule.Port = strings.ReplaceAll(item, "port=", "")
case strings.Contains(item, "protocol="):
itemRule.Protocol = strings.ReplaceAll(item, "protocol=", "")
case item == "accept" || item == "drop":
case item == "accept" || item == "drop" || item == "reject":
itemRule.Strategy = item
}
}

View File

@ -1,37 +0,0 @@
package client
import (
"fmt"
"strings"
"testing"
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
)
func TestFire(t *testing.T) {
ConnInfo := ssh.ConnInfo{
Addr: "172.16.10.234",
User: "ubuntu",
AuthMode: "password",
Port: 22,
}
output, err := ConnInfo.Run("sudo ufw status verbose")
if err != nil {
fmt.Println(err)
}
lines := strings.Split(string(output), "\n")
var datas []FireInfo
isStart := false
for _, line := range lines {
if strings.HasPrefix(line, "--") {
isStart = true
continue
}
if !isStart {
continue
}
}
fmt.Println(datas)
}

View File

@ -6,6 +6,9 @@ type FireInfo struct {
Port string `json:"port"`
Protocol string `json:"protocol"` // tcp udp tcp/udp
Strategy string `json:"strategy"` // accept drop
APPName string `json:"appName"`
IsUsed bool `json:"isUsed"`
}
type Forward struct {

View File

@ -85,6 +85,11 @@ func (f *Ufw) UpdatePingStatus(enabel string) error {
return err
}
stdout, err := cmd.Exec("sudo ufw reload")
if err != nil {
return fmt.Errorf("reload ufw setting failed, err: %v", stdout)
}
return nil
}

View File

@ -69,6 +69,8 @@ export namespace Host {
port: string;
protocol: string;
strategy: string;
appName: string;
isUsed: boolean;
}
export interface RulePort {
operation: string;

View File

@ -1185,19 +1185,33 @@ const message = {
cookieBlockList: 'Cookie Blacklist',
firewall: 'Firewall',
used: 'Used',
unUsed: 'Unused',
firewallHelper: '{0} System firewall',
firewallNotStart: 'The firewall service is not enabled at present, please enable it first!',
stopFirewallHelper:
'If the firewall is disabled, the server loses security protection. Do you want to continue?',
'After the system firewall is disabled, the server loses security protection. Do you want to continue?',
startFirewallHelper:
'After the firewall is enabled, the current server security can be better protected. Do you want to continue?',
'After the firewall is enabled, the server security can be better protected. Do you want to continue?',
noPing: 'Disable ping',
noPingHelper:
'If the ping function is disabled, the server cannot be pinged. Do you want to continue the operation?',
noPingTitle: 'Disable ping',
noPingHelper: 'If the ping function is disabled, the server cannot be pinged. Do you want to continue?',
onPingHelper: 'If you disable ping, hackers may discover your server. Do you want to continue?',
protocol: 'Protocol',
port: 'Port',
changeStrategy: 'Change the {0} strategy',
changeStrategyIPHelper1:
'Change the IP address strategy to [deny]. After the IP address is set, access to the server is prohibited. Do you want to continue?',
changeStrategyIPHelper2:
'Change the IP address strategy to [allow]. After the IP address is set, normal access is restored. Do you want to continue?',
changeStrategyPortHelper1:
'Change the port policy to [drop]. After the port policy is set, external access is denied. Do you want to continue?',
changeStrategyPortHelper2:
'Change the port policy to [accept]. After the policy is set, normal port access will be restored. Do you want to continue?',
stop: 'Stop',
portFormatError: 'Please enter the correct port information!',
portHelper1: 'Multiple ports, such as 8080 and 8081',
portHelper2: 'Range port, such as 8080-8089',
changeStrategyHelper:
'Change [{1}] {0} strategy to [{2}]. After setting, {0} will access {2} externally. Do you want to continue?',
portHelper: 'Multiple ports can be entered, such as 80,81, or range ports, such as 80-88',
@ -1209,9 +1223,10 @@ const message = {
address: 'Specified IP',
allow: 'Allow',
deny: 'Deny',
addressHelper1: 'Support for multiple IP, such as 172.16.10.11 172.16.10.99',
addressHelper2: 'You can enter an IP address segment, for example, 172.16.10.0/24',
addressHelper3: 'You can enter an IP address range, such as 172.16.10.11-172.16.10.99',
addressFormatError: 'Please enter a valid ip address!',
addressHelper1: 'Multiple IP please separated with ",", such as 172.16.10.11, 172.16.10.99',
addressHelper2: 'IP segment, such as 172.16.10.0/24',
addressHelper3: 'IP address range, such as 172.16.10.11-172.16.10.99',
allIP: 'All IP',
portRule: 'Port rule',
ipRule: 'IP rule',

View File

@ -807,6 +807,8 @@ const message = {
LOCAL: '服务器磁盘',
currentPath: '当前路径',
OSS: '阿里云 OSS',
COS: '腾讯云 cos browser',
KODO: '七牛云 Kodo',
S3: '亚马逊 S3 云存储',
MINIO: 'MINIO',
SFTP: 'SFTP',
@ -1184,18 +1186,27 @@ const message = {
cookieBlockList: 'Cookie 黑名单',
firewall: '防火墙',
used: '已使用',
unUsed: '未使用',
firewallHelper: '{0}系统防火墙',
firewallNotStart: '当前未开启防火墙服务请先开启',
stopFirewallHelper: '停用系统防火墙服务器将失去安全防护是否继续操作',
startFirewallHelper: '启用系统防火墙后可以更好的防护当前的服务器安全是否继续操作',
stopFirewallHelper: '系统防火墙关闭后服务器将失去安全防护是否继续',
startFirewallHelper: '系统防火墙开启可以更好的防护服务器安全是否继续',
noPing: ' ping',
noPingHelper: ' ping 后不影响服务器正常使用但无法 ping 通服务器是否继续操作',
onPingHelper: '解除禁 ping 状态可能会被黑客发现您的服务器是否继续操作',
noPingTitle: '是否禁 ping',
noPingHelper: ' ping 后将无法 ping 通服务器是否继续',
onPingHelper: '解除禁 ping 后您的服务器可能会被黑客发现是否继续',
protocol: '协议',
port: '端口',
changeStrategy: '修改{0}策略',
changeStrategyHelper: '修改 [{1}] {0}策略为 [{2}]设置后该{0}{2}外部访问是否继续操作',
portHelper: '支持输入多个端口 80,81 或者范围端口 80-88',
changeStrategyIPHelper1: 'IP 策略修改为屏蔽设置后该 IP 将禁止访问服务器是否继续',
changeStrategyIPHelper2: 'IP 策略修改为放行设置后该 IP 将恢复正常访问是否继续',
changeStrategyPortHelper1: '端口策略修改为拒绝设置后端口将拒绝外部访问是否继续',
changeStrategyPortHelper2: '端口策略为允许设置后端口将恢复正常访问是否继续',
stop: '禁止',
portFormatError: '请输入正确的端口信息',
portHelper1: '多个端口8080,8081',
portHelper2: '范围端口8080-8089',
strategy: '策略',
accept: '允许',
drop: '拒绝',
@ -1204,9 +1215,10 @@ const message = {
address: '指定 IP',
allow: '放行',
deny: '屏蔽',
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',
addressFormatError: '请输入合法的 ip 地址',
addressHelper1: '多个 IP 请用 "," 隔开172.16.10.11,172.16.10.99',
addressHelper2: 'IP 172.16.0.0/24',
addressHelper3: 'IP 范围172.16.10.10-172.16.10.19暂不支持跨网段范围',
allIP: '所有 IP',
portRule: '端口规则',
ipRule: 'IP 规则',

View File

@ -167,6 +167,15 @@ export function checkIp(value: string): boolean {
}
}
export function checkPort(value: string): boolean {
const reg = /^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$/;
if (!reg.test(value) && value !== '') {
return true;
} else {
return false;
}
}
export function getProvider(provider: string): string {
switch (provider) {
case 'dnsAccount':

View File

@ -1,5 +1,5 @@
<template>
<div v-loading="loading">
<div v-loading="loading" style="position: relative">
<FireRouter />
<FireStatus ref="fireStatuRef" @search="search" v-model:loading="loading" v-model:status="fireStatus" />
@ -108,6 +108,12 @@ const paginationConfig = reactive({
});
const search = async () => {
if (fireStatus.value !== 'running') {
loading.value = false;
data.value = [];
paginationConfig.total = 0;
return;
}
let params = {
type: activeTag.value,
info: searchName.value,
@ -141,15 +147,14 @@ const onOpenDialog = async (
};
const onChangeStatus = async (row: Host.RuleInfo, status: string) => {
let operation = status === 'accept' ? i18n.global.t('firewall.allow') : i18n.global.t('firewall.deny');
ElMessageBox.confirm(
i18n.global.t('firewall.changeStrategyHelper', ['IP', row.address, operation]),
i18n.global.t('firewall.changeStrategy', ['IP']),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
).then(async () => {
let operation =
status === 'accept'
? i18n.global.t('firewall.changeStrategyIPHelper2')
: i18n.global.t('firewall.changeStrategyIPHelper1');
ElMessageBox.confirm(operation, i18n.global.t('firewall.changeStrategy', [' IP ']), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
}).then(async () => {
let params = {
oldRule: {
operation: 'remove',

View File

@ -9,10 +9,11 @@
<el-col :span="22">
<el-form-item :label="$t('firewall.address')" prop="address">
<el-input
:disabled="dialogData.title === 'edit'"
:autosize="{ minRows: 3, maxRows: 6 }"
type="textarea"
clearable
v-model="dialogData.rowData!.address"
v-model.trim="dialogData.rowData!.address"
/>
<span class="input-help">{{ $t('firewall.addressHelper1') }}</span>
<span class="input-help">{{ $t('firewall.addressHelper2') }}</span>
@ -45,10 +46,10 @@ import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message';
import { MsgError, MsgSuccess } from '@/utils/message';
import { Host } from '@/api/interface/host';
import { operateIPRule, updateAddrRule } from '@/api/modules/host';
import { deepCopy } from '@/utils/util';
import { checkIp, deepCopy } from '@/utils/util';
const loading = ref();
const oldRule = ref<Host.RuleIP>();
@ -88,9 +89,25 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
dialogData.value.rowData.operation = 'add';
if (!dialogData.value.rowData) return;
let ips = [];
if (dialogData.value.rowData.address.indexOf('-') !== -1) {
ips = dialogData.value.rowData.address.split('-');
} else if (dialogData.value.rowData.address.indexOf(',') !== -1) {
ips = dialogData.value.rowData.address.split(',');
} else if (dialogData.value.rowData.address.indexOf('/') !== -1) {
ips.push(dialogData.value.rowData.address.split('/')[0]);
} else {
ips.push(dialogData.value.rowData.address);
}
for (const ip of ips) {
if (checkIp(ip)) {
MsgError(i18n.global.t('firewall.addressFormatError'));
return;
}
}
loading.value = true;
if (dialogData.value.title === 'create') {
await operateIPRule(dialogData.value.rowData)
.then(() => {

View File

@ -1,5 +1,5 @@
<template>
<div v-loading="loading">
<div v-loading="loading" style="position: relative">
<FireRouter />
<FireStatus ref="fireStatuRef" @search="search" v-model:loading="loading" v-model:status="fireStatus" />
@ -42,13 +42,24 @@
@search="search"
:data="data"
>
<el-table-column type="selection" fix />
<el-table-column type="selection" :selectable="selectable" fix />
<el-table-column :label="$t('firewall.protocol')" :min-width="90" prop="protocol" />
<el-table-column :label="$t('firewall.port')" :min-width="120" prop="port" />
<el-table-column :label="$t('commons.table.status')" :min-width="120">
<template #default="{ row }">
<el-tag type="info" v-if="row.isUsed">
{{
row.appName ? $t('firewall.used') + ' ( ' + row.appName + ' )' : $t('firewall.used')
}}
</el-tag>
<el-tag type="success" v-else>{{ $t('firewall.unUsed') }}</el-tag>
</template>
</el-table-column>
<el-table-column :min-width="80" :label="$t('firewall.strategy')" prop="strategy">
<template #default="{ row }">
<el-button
v-if="row.strategy === 'accept'"
:disabled="row.appName === '1panel'"
@click="onChangeStatus(row, 'drop')"
link
type="success"
@ -111,6 +122,12 @@ const paginationConfig = reactive({
});
const search = async () => {
if (fireStatus.value !== 'running') {
loading.value = false;
data.value = [];
paginationConfig.total = 0;
return;
}
let params = {
type: activeTag.value,
info: searchName.value,
@ -146,15 +163,14 @@ const onOpenDialog = async (
};
const onChangeStatus = async (row: Host.RuleInfo, status: string) => {
let operation = i18n.global.t('firewall.' + status);
ElMessageBox.confirm(
i18n.global.t('firewall.changeStrategyHelper', [i18n.global.t('firewall.port'), row.port, operation]),
i18n.global.t('firewall.changeStrategy', [i18n.global.t('firewall.port')]),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
).then(async () => {
let operation =
status === 'accept'
? i18n.global.t('firewall.changeStrategyPortHelper2')
: i18n.global.t('firewall.changeStrategyPortHelper1');
ElMessageBox.confirm(operation, i18n.global.t('firewall.changeStrategy', [i18n.global.t('firewall.port')]), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
}).then(async () => {
let params = {
oldRule: {
operation: 'remove',
@ -227,18 +243,28 @@ const onDelete = async (row: Host.RuleInfo | null) => {
});
};
function selectable(row) {
return row.appName !== '1panel';
}
const buttons = [
{
label: i18n.global.t('commons.button.edit'),
click: (row: Host.RulePort) => {
onOpenDialog('edit', row);
},
disabled: (row: any) => {
return row.appName === '1panel';
},
},
{
label: i18n.global.t('commons.button.delete'),
click: (row: Host.RuleInfo) => {
onDelete(row);
},
disabled: (row: any) => {
return row.appName === '1panel';
},
},
];

View File

@ -16,8 +16,13 @@
</el-form-item>
<el-form-item :label="$t('firewall.port')" prop="port">
<el-input clearable v-model.trim="dialogData.rowData!.port" />
<span class="input-help">{{ $t('firewall.portHelper') }}</span>
<el-input
:disabled="dialogData.title === 'edit'"
clearable
v-model.trim="dialogData.rowData!.port"
/>
<span class="input-help">{{ $t('firewall.portHelper1') }}</span>
<span class="input-help">{{ $t('firewall.portHelper2') }}</span>
</el-form-item>
<el-form-item :label="$t('firewall.source')" prop="source">
@ -62,10 +67,10 @@ import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message';
import { MsgError, MsgSuccess } from '@/utils/message';
import { Host } from '@/api/interface/host';
import { operatePortRule, updatePortRule } from '@/api/modules/host';
import { deepCopy } from '@/utils/util';
import { checkPort, deepCopy } from '@/utils/util';
const loading = ref();
const oldRule = ref<Host.RulePort>();
@ -102,7 +107,7 @@ const handleClose = () => {
const rules = reactive({
protocol: [Rules.requiredSelect],
port: [Rules.requiredInput],
address: [Rules.requiredInput],
address: [Rules.ip],
});
type FormInstance = InstanceType<typeof ElForm>;
@ -114,6 +119,23 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!valid) return;
dialogData.value.rowData.operation = 'add';
if (!dialogData.value.rowData) return;
if (dialogData.value.rowData.source === 'anyWhere') {
dialogData.value.rowData.address = '';
}
let ports = [];
if (dialogData.value.rowData.port.indexOf('-') !== -1) {
ports = dialogData.value.rowData.port.split('-');
} else if (dialogData.value.rowData.port.indexOf(',') !== -1) {
ports = dialogData.value.rowData.port.split(',');
} else {
ports.push(dialogData.value.rowData.port);
}
for (const port of ports) {
if (checkPort(port)) {
MsgError(i18n.global.t('firewall.portFormatError'));
return;
}
}
loading.value = true;
if (dialogData.value.title === 'create') {
await operatePortRule(dialogData.value.rowData)

View File

@ -16,22 +16,21 @@
<el-button type="primary" @click="onOperate('stop')" link>
{{ $t('commons.button.stop') }}
</el-button>
<el-divider direction="vertical" />
<el-button type="primary" link>{{ $t('firewall.noPing') }}</el-button>
<el-switch
style="margin-left: 10px"
inactive-value="Disable"
active-value="Enable"
@change="onPingOperate(baseInfo.pingStatus)"
v-model="onPing"
/>
</span>
<span v-if="baseInfo.status === 'not running'" class="buttons">
<el-button type="primary" @click="onOperate('start')" link>
{{ $t('commons.button.start') }}
</el-button>
</span>
<el-divider direction="vertical" />
<el-button type="primary" link>{{ $t('firewall.noPing') }}</el-button>
<el-switch
style="margin-left: 10px"
inactive-value="Disable"
active-value="Enable"
@change="onPingOperate(baseInfo.pingStatus)"
v-model="onPing"
/>
</div>
</el-card>
</div>
@ -42,11 +41,12 @@
import { Host } from '@/api/interface/host';
import { loadFireBaseInfo, operateFire } from '@/api/modules/host';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { ElMessageBox } from 'element-plus';
import { ref } from 'vue';
const baseInfo = ref<Host.FirewallBase>({ status: '', name: '', version: '', pingStatus: '' });
const onPing = ref();
const onPing = ref('Disable');
const acceptParams = (): void => {
loadBaseInfo(true);
@ -59,7 +59,7 @@ const loadBaseInfo = async (search: boolean) => {
baseInfo.value = res.data;
onPing.value = baseInfo.value.pingStatus;
emit('update:status', baseInfo.value.status);
if (baseInfo.value.status === 'running' && search) {
if (search) {
emit('search');
} else {
emit('update:loading', false);
@ -81,6 +81,7 @@ const onOperate = async (operation: string) => {
emit('update:status', 'running');
await operateFire(operation)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
loadBaseInfo(true);
})
.catch(() => {
@ -92,7 +93,7 @@ const onOperate = async (operation: string) => {
const onPingOperate = async (operation: string) => {
let operationHelper =
operation === 'Enabel' ? i18n.global.t('firewall.noPingHelper') : i18n.global.t('firewall.onPingHelper');
ElMessageBox.confirm(operationHelper, i18n.global.t('firewall.noPing'), {
ElMessageBox.confirm(operationHelper, i18n.global.t('firewall.noPingTitle'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
})
@ -102,6 +103,7 @@ const onPingOperate = async (operation: string) => {
operation = operation === 'Disable' ? 'enablePing' : 'disablePing';
await operateFire(operation)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
loadBaseInfo(false);
})
.catch(() => {