fix: Add Docker restart option to firewall operations (#10253)

Refs #10247
This commit is contained in:
ssongliu 2025-09-03 17:29:30 +08:00 committed by GitHub
parent 7ba06e73d7
commit ea25b800d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 104 additions and 30 deletions

View file

@ -63,7 +63,7 @@ func (b *BaseApi) OperateFirewall(c *gin.Context) {
return
}
if err := firewallService.OperateFirewall(req.Operation); err != nil {
if err := firewallService.OperateFirewall(req); err != nil {
helper.InternalServer(c, err)
return
}

View file

@ -17,7 +17,8 @@ type RuleSearch struct {
}
type FirewallOperation struct {
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
WithDockerRestart bool `json:"withDockerRestart"`
}
type PortRuleOperate struct {

View file

@ -28,7 +28,7 @@ type FirewallService struct{}
type IFirewallService interface {
LoadBaseInfo() (dto.FirewallBaseInfo, error)
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
OperateFirewall(operation string) error
OperateFirewall(req dto.FirewallOperation) error
OperatePortRule(req dto.PortRuleOperate, reload bool) error
OperateForwardRule(req dto.ForwardRuleOperate) error
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
@ -177,13 +177,13 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
return int64(total), backDatas, nil
}
func (u *FirewallService) OperateFirewall(operation string) error {
func (u *FirewallService) OperateFirewall(req dto.FirewallOperation) error {
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
needRestartDocker := false
switch operation {
switch req.Operation {
case "start":
if err := client.Start(); err != nil {
return err
@ -208,9 +208,9 @@ func (u *FirewallService) OperateFirewall(operation string) error {
case "enablePing":
return u.updatePingStatus("1")
default:
return fmt.Errorf("not supported operation: %s", operation)
return fmt.Errorf("not supported operation: %s", req.Operation)
}
if needRestartDocker {
if needRestartDocker && req.WithDockerRestart {
if err := restartDocker(); err != nil {
return err
}

View file

@ -12,8 +12,15 @@ export const loadFireBaseInfo = () => {
export const searchFireRule = (params: Host.RuleSearch) => {
return http.post<ResPage<Host.RuleInfo>>(`/hosts/firewall/search`, params, TimeoutEnum.T_40S);
};
export const operateFire = (operation: string) => {
return http.post(`/hosts/firewall/operate`, { operation: operation }, TimeoutEnum.T_40S);
export const operateFire = (operation: string, withDockerRestart: boolean) => {
return http.post(
`/hosts/firewall/operate`,
{
operation: operation,
withDockerRestart: withDockerRestart,
},
TimeoutEnum.T_60S,
);
};
export const operatePortRule = (params: Host.RulePort) => {
return http.post<Host.RulePort>(`/hosts/firewall/port`, params, TimeoutEnum.T_40S);

View file

@ -0,0 +1,50 @@
<template>
<DialogPro v-model="open" size="small">
<el-form ref="formRef" label-position="top" @submit.prevent>
<el-form-item :label="title">
<el-radio-group v-model="restart">
<el-radio :value="true">{{ $t('setting.restartNow') }}</el-radio>
<el-radio :value="false">{{ $t('setting.restartLater') }}</el-radio>
</el-radio-group>
<span class="input-help" v-if="restart">{{ $t('xpack.node.syncProxyHelper1') }}</span>
<span class="input-help" v-else>{{ $t('xpack.node.syncProxyHelper2') }}</span>
</el-form-item>
</el-form>
<slot name="helper" />
<template #footer>
<span class="dialog-footer">
<el-button @click="open = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="onConfirm">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</DialogPro>
</template>
<script lang="ts" setup>
const open = ref(false);
const title = ref();
const restart = ref(true);
const em = defineEmits(['update:withDockerRestart', 'submit']);
interface DialogProps {
title: string;
}
const acceptParams = async (params: DialogProps): Promise<void> => {
title.value = params.title;
open.value = true;
};
const onConfirm = async () => {
em('update:withDockerRestart', restart.value);
em('submit');
open.value = false;
};
defineExpose({
acceptParams,
});
</script>

View file

@ -2725,6 +2725,7 @@ const message = {
quickJump: 'Quick access',
used: 'Used',
unUsed: 'Unused',
dockerRestart: 'Firewall operations require restarting the Docker service',
firewallHelper: '{0} system firewall',
firewallNotStart: `The system firewall isn't enabled at present. Enable it first.`,
restartFirewallHelper: 'This operation will restart the current firewall. Do you want to continue?',

View file

@ -2640,6 +2640,7 @@ const message = {
quickJump: 'クイックアクセス',
used: '使用済み',
unUsed: '未使用',
dockerRestart: 'ファイアウォール操作にはDockerサービスの再起動が必要です',
firewallHelper: '{0}システムファイアウォール',
firewallNotStart: `現在、システムファイアウォールは有効になっていません。最初に有効にします。`,
restartFirewallHelper: 'この操作は現在のファイアウォールを再起動します続けたいですか',

View file

@ -2593,6 +2593,7 @@ const message = {
quickJump: '빠른 접근',
used: '사용됨',
unUsed: '사용 ',
dockerRestart: '방화벽 작업에는 Docker 서비스 재시작이 필요합니다',
firewallHelper: '{0} 시스템 방화벽',
firewallNotStart: '현재 시스템 방화벽이 활성화되지 않았습니다. 먼저 활성화하세요.',
restartFirewallHelper: ' 작업은 현재 방화벽을 재시작합니다. 계속하시겠습니까?',

View file

@ -2700,6 +2700,7 @@ const message = {
quickJump: 'Akses pantas',
used: 'Digunakan',
unUsed: 'Tidak Digunakan',
dockerRestart: 'Operasi firewall memerlukan memulakan semula perkhidmatan Docker',
firewallHelper: '{0} firewall sistem',
firewallNotStart: `Firewall sistem belum diaktifkan. Aktifkannya dahulu.`,
restartFirewallHelper: 'Operasi ini akan memulakan semula firewall semasa. Adakah anda mahu meneruskan?',

View file

@ -2703,6 +2703,7 @@ const message = {
quickJump: 'Acesso rápido',
used: 'Usado',
unUsed: 'Não usado',
dockerRestart: 'Operações de firewall exigem reinicialização do serviço Docker',
firewallHelper: 'Firewall do sistema {0}',
firewallNotStart: 'O firewall do sistema não está habilitado atualmente. Habilite-o primeiro.',
restartFirewallHelper: 'Esta operação reiniciará o firewall atual. Deseja continuar?',

View file

@ -2697,6 +2697,7 @@ const message = {
quickJump: 'Быстрый доступ',
used: 'Используется',
unUsed: 'Не используется',
dockerRestart: 'Операции с брандмауэром требуют перезапуска службы Docker',
firewallHelper: '{0} межсетевой экран',
firewallNotStart: 'Межсетевой экран в настоящее время не включен. Сначала включите его.',
restartFirewallHelper: 'Эта операция перезапустит текущий межсетевой экран. Хотите продолжить?',

View file

@ -2759,6 +2759,7 @@ const message = {
quickJump: 'Hızlı erişim',
used: 'Kullanıldı',
unUsed: 'Kullanılmadı',
dockerRestart: 'Güvenlik duvarı işlemleri Docker hizmetinin yeniden başlatılmasını gerektirir',
firewallHelper: '{0} sistem güvenlik duvarı',
firewallNotStart: 'Sistem güvenlik duvarı şu anda etkin değil. Önce etkinleştirin.',
restartFirewallHelper: 'Bu işlem mevcut güvenlik duvarını yeniden başlatacak. Devam etmek istiyor musunuz?',

View file

@ -2541,6 +2541,7 @@ const message = {
quickJump: '快速跳轉',
used: '已使用',
unUsed: '未使用',
dockerRestart: '防火牆操作需要重啟 Docker 服務',
firewallHelper: '{0}系統防火墻',
firewallNotStart: '當前未開啟系統防火墻請先開啟',
restartFirewallHelper: '該操作將對當前防火牆進行重啟操作是否繼續',

View file

@ -2531,6 +2531,7 @@ const message = {
quickJump: '快速跳转',
used: '已使用',
unUsed: '未使用',
dockerRestart: '防火墙操作需要重启 Docker 服务',
firewallHelper: '{0}系统防火墙',
firewallNotStart: '当前未开启系统防火墙请先开启',
restartFirewallHelper: '该操作将对当前防火墙进行重启操作是否继续',

View file

@ -36,6 +36,16 @@
</el-card>
</div>
<NoSuchService v-if="!baseInfo.isExist" name="Firewalld / Ufw" />
<DockerRestart
ref="dockerRef"
v-model:withDockerRestart="withDockerRestart"
@submit="onSubmit"
:title="$t('firewall.firewallHelper', [i18n.global.t('commons.button.' + operation)])"
>
<template #helper>
<span>{{ $t('firewall.' + operation + 'FirewallHelper') }}</span>
</template>
</DockerRestart>
</div>
</template>
@ -44,6 +54,7 @@ import { Host } from '@/api/interface/host';
import { loadFireBaseInfo, operateFire } from '@/api/modules/host';
import i18n from '@/lang';
import NoSuchService from '@/components/layout-content/no-such-service.vue';
import DockerRestart from '@/components/docker-proxy/docker-restart.vue';
import { MsgSuccess } from '@/utils/message';
import { ElMessageBox } from 'element-plus';
import { ref } from 'vue';
@ -51,6 +62,9 @@ import { ref } from 'vue';
const baseInfo = ref<Host.FirewallBase>({ isActive: false, isExist: true, name: '', version: '', pingStatus: '' });
const onPing = ref('Disable');
const oldStatus = ref();
const dockerRef = ref();
const operation = ref('restart');
const withDockerRestart = ref(false);
const acceptParams = (): void => {
loadBaseInfo(true);
@ -78,28 +92,21 @@ const loadBaseInfo = async (search: boolean) => {
});
};
const onOperate = async (operation: string) => {
emit('update:maskShow', false);
let operationHelper = i18n.global.t('firewall.' + operation + 'FirewallHelper');
let title = i18n.global.t('firewall.firewallHelper', [i18n.global.t('commons.button.' + operation)]);
ElMessageBox.confirm(operationHelper, title, {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
})
.then(async () => {
emit('update:loading', true);
emit('update:maskShow', true);
await operateFire(operation)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
loadBaseInfo(true);
})
.catch(() => {
loadBaseInfo(true);
});
const onOperate = async (op: string) => {
operation.value = op;
dockerRef.value.acceptParams({ title: i18n.global.t('firewall.dockerRestart') });
};
const onSubmit = async () => {
emit('update:loading', true);
emit('update:maskShow', true);
await operateFire(operation.value, withDockerRestart.value)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
loadBaseInfo(true);
})
.catch(() => {
emit('update:maskShow', true);
loadBaseInfo(true);
});
};
@ -115,7 +122,7 @@ const onPingOperate = async (operation: string) => {
emit('update:loading', true);
operation = operation === 'Disable' ? 'disablePing' : 'enablePing';
emit('update:maskShow', true);
await operateFire(operation)
await operateFire(operation, false)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
loadBaseInfo(false);