feat: Complete the Docker proxy synchronization function (#8480)

This commit is contained in:
ssongliu 2025-04-25 22:37:54 +08:00 committed by GitHub
parent f485b1729c
commit 4aa628121c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 55 additions and 112 deletions

View file

@ -151,6 +151,7 @@ type ProxyUpdate struct {
ProxyUser string `json:"proxyUser"` ProxyUser string `json:"proxyUser"`
ProxyPasswd string `json:"proxyPasswd"` ProxyPasswd string `json:"proxyPasswd"`
ProxyPasswdKeep string `json:"proxyPasswdKeep"` ProxyPasswdKeep string `json:"proxyPasswdKeep"`
ProxyDocker bool `json:"proxyDocker"`
} }
type CleanData struct { type CleanData struct {

View file

@ -199,6 +199,9 @@ func (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error {
if err := settingRepo.Update("ProxyPasswdKeep", req.ProxyPasswdKeep); err != nil { if err := settingRepo.Update("ProxyPasswdKeep", req.ProxyPasswdKeep); err != nil {
return err return err
} }
if err := xpack.ProxyDocker(loadDockerProxy(req)); err != nil {
return err
}
go func() { go func() {
if err := xpack.Sync(constant.SyncSystemProxy); err != nil { if err := xpack.Sync(constant.SyncSystemProxy); err != nil {
global.LOG.Errorf("sync proxy to node failed, err: %v", err) global.LOG.Errorf("sync proxy to node failed, err: %v", err)
@ -636,3 +639,18 @@ func (u *SettingService) GetLoginSetting() (*dto.SystemSetting, error) {
} }
return res, nil return res, nil
} }
func loadDockerProxy(req dto.ProxyUpdate) string {
if len(req.ProxyType) == 0 || req.ProxyType == "close" || !req.ProxyDocker {
return ""
}
proxyPasswd := ""
if len(req.ProxyUser) != 0 {
proxyPasswd = req.ProxyPasswd + "@"
}
proxyUrl := req.ProxyType + "://" + req.ProxyUser + ":" + proxyPasswd + req.ProxyUrl + req.ProxyPort
if req.ProxyType == "http" || req.ProxyType == "https" {
req.ProxyUrl = req.ProxyType + "://" + req.ProxyUrl
}
return proxyUrl
}

View file

@ -45,6 +45,7 @@ const (
const ( const (
SyncSystemProxy = "SyncSystemProxy" SyncSystemProxy = "SyncSystemProxy"
SyncBackupAccounts = "SyncBackupAccounts" SyncBackupAccounts = "SyncBackupAccounts"
SyncAlertSetting = "SyncAlertSetting"
) )
var WebUrlMap = map[string]struct{}{ var WebUrlMap = map[string]struct{}{

View file

@ -14,6 +14,8 @@ import (
func Proxy(c *gin.Context, currentNode string) {} func Proxy(c *gin.Context, currentNode string) {}
func ProxyDocker(proxyURL string) error { return nil }
func UpdateGroup(name string, group, newGroup uint) error { return nil } func UpdateGroup(name string, group, newGroup uint) error { return nil }
func CheckBackupUsed(name string) error { return nil } func CheckBackupUsed(name string) error { return nil }

View file

@ -3265,8 +3265,9 @@ const message = {
nodeInfo: 'Node Information', nodeInfo: 'Node Information',
syncInfo: 'Data synchronization', syncInfo: 'Data synchronization',
syncHelper: 'When master node data changes, it synchronizes to this child node in real-time', syncHelper: 'When master node data changes, it synchronizes to this child node in real-time',
syncBackupAccount: 'Backup account data', syncBackupAccount: 'Backup account settings',
syncProxy: 'System proxy data', syncProxy: 'System proxy settings',
syncAlertSetting: 'System alert settings',
syncNodeInfo: 'Node basic data,', syncNodeInfo: 'Node basic data,',
nodeSyncHelper: 'Node information synchronization will sync the following information:', nodeSyncHelper: 'Node information synchronization will sync the following information:',
nodeSyncHelper1: '1. Public backup account information', nodeSyncHelper1: '1. Public backup account information',

View file

@ -3124,8 +3124,9 @@ const message = {
nodeInfo: 'ノード情報', nodeInfo: 'ノード情報',
syncInfo: 'データ同期,', syncInfo: 'データ同期,',
syncHelper: 'マスターノードのデータが変更されるとこの子ノードにリアルタイムで同期されます,', syncHelper: 'マスターノードのデータが変更されるとこの子ノードにリアルタイムで同期されます,',
syncBackupAccount: 'アカウントデータのバックアップ,', syncBackupAccount: 'バックアップアカウント設定',
syncProxy: 'システムプロキシデータ,', syncProxy: 'システムプロキシ設定',
syncAlertSetting: 'システムアラート設定',
syncNodeInfo: 'ノード基本データ,', syncNodeInfo: 'ノード基本データ,',
nodeSyncHelper: 'ノード情報の同期は以下の情報を同期します', nodeSyncHelper: 'ノード情報の同期は以下の情報を同期します',
nodeSyncHelper1: '1. 公共のバックアップアカウント情報', nodeSyncHelper1: '1. 公共のバックアップアカウント情報',

View file

@ -3072,8 +3072,9 @@ const message = {
nodeInfo: '노드 정보', nodeInfo: '노드 정보',
syncInfo: '데이터 동기화,', syncInfo: '데이터 동기화,',
syncHelper: '마스터 노드 데이터가 변경되면, 자식 노드에 실시간으로 동기화됩니다,', syncHelper: '마스터 노드 데이터가 변경되면, 자식 노드에 실시간으로 동기화됩니다,',
syncBackupAccount: '계정 데이터 백업,', syncBackupAccount: '백업 계정 설정',
syncProxy: '시스템 프록시 데이터,', syncProxy: '시스템 프록시 설정',
syncAlertSetting: '시스템 경고 설정',
syncNodeInfo: '노드 기본 데이터,', syncNodeInfo: '노드 기본 데이터,',
nodeSyncHelper: '노드 정보 동기화는 다음 정보를 동기화합니다:', nodeSyncHelper: '노드 정보 동기화는 다음 정보를 동기화합니다:',
nodeSyncHelper1: '1. 공용 백업 계정 정보', nodeSyncHelper1: '1. 공용 백업 계정 정보',

View file

@ -3193,8 +3193,9 @@ const message = {
nodeInfo: 'Maklumat Nod', nodeInfo: 'Maklumat Nod',
syncInfo: 'Penyegerakan data,', syncInfo: 'Penyegerakan data,',
syncHelper: 'Apabila data nod induk berubah, ia akan disegerakkan ke nod anak ini secara masa nyata,', syncHelper: 'Apabila data nod induk berubah, ia akan disegerakkan ke nod anak ini secara masa nyata,',
syncBackupAccount: 'Data akaun sandaran,', syncBackupAccount: 'Tetapan akaun sandaran',
syncProxy: 'Data proksi sistem,', syncProxy: 'Tetapan proksi sistem',
syncAlertSetting: 'Tetapan amaran sistem',
syncNodeInfo: 'Data asas nod,', syncNodeInfo: 'Data asas nod,',
nodeSyncHelper: 'Penyelarasan maklumat nod akan menyelaraskan maklumat berikut:', nodeSyncHelper: 'Penyelarasan maklumat nod akan menyelaraskan maklumat berikut:',
nodeSyncHelper1: '1. Maklumat akaun sandaran awam', nodeSyncHelper1: '1. Maklumat akaun sandaran awam',

View file

@ -3199,8 +3199,9 @@ const message = {
nodeInfo: 'Informações do ', nodeInfo: 'Informações do ',
syncInfo: 'Sincronização de dados,', syncInfo: 'Sincronização de dados,',
syncHelper: 'Quando os dados do mestre mudam, são sincronizados em tempo real para este filho,', syncHelper: 'Quando os dados do mestre mudam, são sincronizados em tempo real para este filho,',
syncBackupAccount: 'Dados de conta de backup,', syncBackupAccount: 'Configurações de conta de backup',
syncProxy: 'Dados de proxy do sistema,', syncProxy: 'Configurações de proxy do sistema',
syncAlertSetting: 'Configurações de alerta do sistema',
syncNodeInfo: 'Dados básicos do ,', syncNodeInfo: 'Dados básicos do ,',
nodeSyncHelper: 'A sincronização das informações do irá sincronizar as seguintes informações:', nodeSyncHelper: 'A sincronização das informações do irá sincronizar as seguintes informações:',
nodeSyncHelper1: '1. Informações da conta de backup pública', nodeSyncHelper1: '1. Informações da conta de backup pública',

View file

@ -3188,8 +3188,9 @@ const message = {
syncInfo: 'Синхронизация данных,', syncInfo: 'Синхронизация данных,',
syncHelper: syncHelper:
'При изменении данных главного узла, происходит синхронизация с этим дочерним узлом в реальном времени,', 'При изменении данных главного узла, происходит синхронизация с этим дочерним узлом в реальном времени,',
syncBackupAccount: 'Резервные данные аккаунта,', syncBackupAccount: 'Настройки резервной учётной записи',
syncProxy: 'Данные системного прокси,', syncProxy: 'Настройки системного прокси',
syncAlertSetting: 'Настройки системных предупреждений',
syncNodeInfo: 'Базовые данные узла,', syncNodeInfo: 'Базовые данные узла,',
nodeSyncHelper: 'Синхронизация информации о узле будет синхронизировать следующую информацию:', nodeSyncHelper: 'Синхронизация информации о узле будет синхронизировать следующую информацию:',
nodeSyncHelper1: '1. Информация о публичной резервной учетной записи', nodeSyncHelper1: '1. Информация о публичной резервной учетной записи',

View file

@ -3028,8 +3028,9 @@ const message = {
nodeInfo: '節點資訊', nodeInfo: '節點資訊',
syncInfo: '數據同步,', syncInfo: '數據同步,',
syncHelper: '當主節點數據發生變化時實時同步到該子節點,', syncHelper: '當主節點數據發生變化時實時同步到該子節點,',
syncBackupAccount: '備份帳號數據,', syncBackupAccount: '備份帳號設定',
syncProxy: '系統代理數據,', syncProxy: '系統代理設定',
syncAlertSetting: '系統告警設定',
syncNodeInfo: '節點基礎數據,', syncNodeInfo: '節點基礎數據,',
nodeSyncHelper: '節點信息同步將同步以下信息', nodeSyncHelper: '節點信息同步將同步以下信息',
nodeSyncHelper1: '1. 公用的備份帳號信息', nodeSyncHelper1: '1. 公用的備份帳號信息',

View file

@ -3011,8 +3011,9 @@ const message = {
nodeInfo: '节点信息', nodeInfo: '节点信息',
syncInfo: '数据同步', syncInfo: '数据同步',
syncHelper: '主节点数据发生变化时实时同步到该子节点', syncHelper: '主节点数据发生变化时实时同步到该子节点',
syncBackupAccount: '备份账号数据', syncBackupAccount: '备份账号设置',
syncProxy: '系统代理数据', syncProxy: '系统代理设置',
syncAlertSetting: '系统告警设置',
syncNodeInfo: '节点基础数据', syncNodeInfo: '节点基础数据',
nodeSyncHelper: '节点信息同步将同步以下信息', nodeSyncHelper: '节点信息同步将同步以下信息',
nodeSyncHelper1: '1. 公用的备份账号信息', nodeSyncHelper1: '1. 公用的备份账号信息',

View file

@ -1,5 +1,5 @@
<template> <template>
<DrawerPro v-model="passwordVisible" :header="$t('setting.proxy')" @close="handleClose" size="large"> <DrawerPro v-model="proxyVisible" :header="$t('setting.proxy')" @close="handleClose" size="large">
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" v-loading="loading"> <el-form ref="formRef" label-position="top" :model="form" :rules="rules" v-loading="loading">
<el-alert class="common-prompt" :closable="false" type="warning"> <el-alert class="common-prompt" :closable="false" type="warning">
<template #default> <template #default>
@ -46,45 +46,13 @@
<el-checkbox v-model="form.proxyPasswdKeepItem" :label="$t('setting.proxyPasswdKeep')" /> <el-checkbox v-model="form.proxyPasswdKeepItem" :label="$t('setting.proxyPasswdKeep')" />
</el-form-item> </el-form-item>
<el-form-item v-if="isMasterProductPro"> <el-form-item v-if="isMasterProductPro">
<el-checkbox v-model="form.proxyDocker" @change="onChange" :label="$t('setting.proxyDocker')" /> <el-checkbox v-model="form.proxyDocker" :label="$t('setting.proxyDocker')" />
<span class="input-help">{{ $t('setting.proxyDockerHelper') }}</span> <span class="input-help">{{ $t('setting.proxyDockerHelper') }}</span>
</el-form-item> </el-form-item>
<div v-if="form.proxyDocker">
<el-form-item prop="syncToNode">
<el-switch
@change="getNodes"
v-model="form.syncToNode"
active-value="Enable"
inactive-value="Disable"
></el-switch>
<span class="ml-2.5 el-checkbox">{{ $t('setting.syncToNode') }}</span>
<span class="input-help">{{ $t('setting.syncToNodeHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.nodes')" prop="nodes" v-if="form.syncToNode === 'Enable'">
<el-row :gutter="20">
<el-col :span="12">
<el-select v-model="form.selectNode" class="p-w-200">
<el-option :label="$t('commons.table.all')" value="all"></el-option>
<el-option :label="$t('setting.selectNode')" value="select"></el-option>
</el-select>
</el-col>
<el-col :span="12" v-if="form.selectNode === 'select'">
<el-select v-model="form.nodes" multiple class="p-w-200">
<el-option
v-for="item in nodeList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-col>
</el-row>
</el-form-item>
</div>
</div> </div>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button :disabled="loading" @click="passwordVisible = false"> <el-button :disabled="loading" @click="proxyVisible = false">
{{ $t('commons.button.cancel') }} {{ $t('commons.button.cancel') }}
</el-button> </el-button>
<el-button :disabled="loading" type="primary" @click="submitChangePassword(formRef)"> <el-button :disabled="loading" type="primary" @click="submitChangePassword(formRef)">
@ -102,13 +70,10 @@ import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus'; import { FormInstance } from 'element-plus';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { listNodeOptions, updateProxy } from '@/api/modules/setting'; import { updateProxy } from '@/api/modules/setting';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { updateDaemonJson } from '@/api/modules/container';
import ConfirmDialog from '@/components/confirm-dialog/index.vue'; import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { escapeProxyURL } from '@/utils/util';
import { updateDockerProxySetting } from '@/xpack/api/modules/setting';
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const emit = defineEmits<{ (e: 'search'): void }>(); const emit = defineEmits<{ (e: 'search'): void }>();
@ -123,8 +88,7 @@ const rules = reactive({
}); });
const loading = ref(false); const loading = ref(false);
const passwordVisible = ref<boolean>(false); const proxyVisible = ref<boolean>(false);
const proxyDockerVisible = ref<boolean>(false);
const form = reactive({ const form = reactive({
proxyUrl: '', proxyUrl: '',
proxyType: '', proxyType: '',
@ -135,11 +99,7 @@ const form = reactive({
proxyPasswdKeep: '', proxyPasswdKeep: '',
proxyPasswdKeepItem: false, proxyPasswdKeepItem: false,
proxyDocker: false, proxyDocker: false,
syncToNode: 'Enable',
selectNode: 'all',
nodes: [],
}); });
const nodeList = ref([]);
interface DialogProps { interface DialogProps {
url: string; url: string;
@ -149,8 +109,6 @@ interface DialogProps {
passwd: string; passwd: string;
passwdKeep: string; passwdKeep: string;
proxyDocker: string; proxyDocker: string;
proxyDockerSyncToNode: string;
proxyDockerSyncNodes: string;
} }
const acceptParams = (params: DialogProps): void => { const acceptParams = (params: DialogProps): void => {
if (params.url) { if (params.url) {
@ -167,24 +125,11 @@ const acceptParams = (params: DialogProps): void => {
form.proxyUser = params.user; form.proxyUser = params.user;
form.proxyPasswd = params.passwd; form.proxyPasswd = params.passwd;
form.proxyDocker = params.proxyDocker !== ''; form.proxyDocker = params.proxyDocker !== '';
if (form.proxyDocker) {
getNodes();
form.syncToNode = params.proxyDockerSyncToNode;
form.nodes = params.proxyDockerSyncNodes ? JSON.parse(params.proxyDockerSyncNodes) : [];
form.selectNode = form.nodes.length > 0 ? 'select' : 'all';
}
proxyDockerVisible.value = params.proxyDocker !== ''; proxyVisible.value = true;
passwordVisible.value = true;
form.proxyPasswdKeepItem = params.passwdKeep === 'Enable'; form.proxyPasswdKeepItem = params.passwdKeep === 'Enable';
}; };
const getNodes = () => {
if (form.syncToNode === 'Enable' && nodeList.value.length === 0) {
listNodes();
}
};
const submitChangePassword = async (formEl: FormInstance | undefined) => { const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
@ -205,13 +150,7 @@ const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (form.proxyType === 'http' || form.proxyType === 'https') { if (form.proxyType === 'http' || form.proxyType === 'https') {
params.proxyUrl = form.proxyType + '://' + form.proxyUrl; params.proxyUrl = form.proxyType + '://' + form.proxyUrl;
} }
if ( if (isMasterProductPro.value && params.proxyDocker) {
isMasterProductPro.value &&
(params.proxyDocker ||
(proxyDockerVisible.value && isClose) ||
(proxyDockerVisible.value && !isClose) ||
(proxyDockerVisible.value && !params.proxyDocker))
) {
let confirmParams = { let confirmParams = {
header: i18n.global.t('setting.confDockerProxy'), header: i18n.global.t('setting.confDockerProxy'),
operationInfo: i18n.global.t('setting.restartNowHelper'), operationInfo: i18n.global.t('setting.restartNowHelper'),
@ -224,10 +163,7 @@ const submitChangePassword = async (formEl: FormInstance | undefined) => {
.then(async () => { .then(async () => {
loading.value = false; loading.value = false;
emit('search'); emit('search');
passwordVisible.value = false; proxyVisible.value = false;
if (isClose) {
await updateDaemonJson(`${form.proxyType}-proxy`, '');
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
}) })
.catch(() => { .catch(() => {
@ -253,26 +189,10 @@ const onSubmit = async () => {
if (!isClose) { if (!isClose) {
params.proxyPasswdKeep = form.proxyPasswdKeepItem ? 'Enable' : 'Disable'; params.proxyPasswdKeep = form.proxyPasswdKeepItem ? 'Enable' : 'Disable';
} }
let proxyPort = params.proxyPort ? `:${params.proxyPort}` : '';
let proxyUser = params.proxyUser ? `${escapeProxyURL(params.proxyUser)}` : '';
let proxyPasswd = '';
if (params.proxyUser) {
proxyPasswd = params.proxyPasswd ? `:${escapeProxyURL(params.proxyPasswd)}@` : '@';
}
let proxyUrl = form.proxyType + '://' + proxyUser + proxyPasswd + form.proxyUrl + proxyPort;
if (form.proxyType === 'http' || form.proxyType === 'https') { if (form.proxyType === 'http' || form.proxyType === 'https') {
params.proxyUrl = form.proxyType + '://' + form.proxyUrl; params.proxyUrl = form.proxyType + '://' + form.proxyUrl;
} }
await updateProxy(params); await updateProxy(params);
if (isClose || params.proxyDocker === false) {
proxyUrl = '';
}
let param = {
proxyDocker: proxyUrl,
proxyDockerSyncToNode: form.syncToNode,
proxyDockerSyncNodes: JSON.stringify(form.nodes),
};
await updateDockerProxySetting(param);
emit('search'); emit('search');
handleClose(); handleClose();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@ -281,15 +201,8 @@ const onSubmit = async () => {
} }
}; };
const listNodes = async () => {
try {
const res = await listNodeOptions();
nodeList.value = res.data || [];
} catch (error) {}
};
const handleClose = () => { const handleClose = () => {
passwordVisible.value = false; proxyVisible.value = false;
}; };
defineExpose({ defineExpose({