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 return
} }
if err := firewallService.OperateFirewall(req.Operation); err != nil { if err := firewallService.OperateFirewall(req); err != nil {
helper.InternalServer(c, err) helper.InternalServer(c, err)
return return
} }

View file

@ -18,6 +18,7 @@ type RuleSearch struct {
type FirewallOperation 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 { type PortRuleOperate struct {

View file

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

View file

@ -12,8 +12,15 @@ export const loadFireBaseInfo = () => {
export const searchFireRule = (params: Host.RuleSearch) => { export const searchFireRule = (params: Host.RuleSearch) => {
return http.post<ResPage<Host.RuleInfo>>(`/hosts/firewall/search`, params, TimeoutEnum.T_40S); return http.post<ResPage<Host.RuleInfo>>(`/hosts/firewall/search`, params, TimeoutEnum.T_40S);
}; };
export const operateFire = (operation: string) => { export const operateFire = (operation: string, withDockerRestart: boolean) => {
return http.post(`/hosts/firewall/operate`, { operation: operation }, TimeoutEnum.T_40S); return http.post(
`/hosts/firewall/operate`,
{
operation: operation,
withDockerRestart: withDockerRestart,
},
TimeoutEnum.T_60S,
);
}; };
export const operatePortRule = (params: Host.RulePort) => { export const operatePortRule = (params: Host.RulePort) => {
return http.post<Host.RulePort>(`/hosts/firewall/port`, params, TimeoutEnum.T_40S); 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', quickJump: 'Quick access',
used: 'Used', used: 'Used',
unUsed: 'Unused', unUsed: 'Unused',
dockerRestart: 'Firewall operations require restarting the Docker service',
firewallHelper: '{0} system firewall', firewallHelper: '{0} system firewall',
firewallNotStart: `The system firewall isn't enabled at present. Enable it first.`, 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?', restartFirewallHelper: 'This operation will restart the current firewall. Do you want to continue?',

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2759,6 +2759,7 @@ const message = {
quickJump: 'Hızlı erişim', quickJump: 'Hızlı erişim',
used: 'Kullanıldı', used: 'Kullanıldı',
unUsed: 'Kullanılmadı', unUsed: 'Kullanılmadı',
dockerRestart: 'Güvenlik duvarı işlemleri Docker hizmetinin yeniden başlatılmasını gerektirir',
firewallHelper: '{0} sistem güvenlik duvarı', firewallHelper: '{0} sistem güvenlik duvarı',
firewallNotStart: 'Sistem güvenlik duvarı şu anda etkin değil. Önce etkinleştirin.', 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?', 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: '快速跳轉', quickJump: '快速跳轉',
used: '已使用', used: '已使用',
unUsed: '未使用', unUsed: '未使用',
dockerRestart: '防火牆操作需要重啟 Docker 服務',
firewallHelper: '{0}系統防火墻', firewallHelper: '{0}系統防火墻',
firewallNotStart: '當前未開啟系統防火墻請先開啟', firewallNotStart: '當前未開啟系統防火墻請先開啟',
restartFirewallHelper: '該操作將對當前防火牆進行重啟操作是否繼續', restartFirewallHelper: '該操作將對當前防火牆進行重啟操作是否繼續',

View file

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

View file

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