feat: Sync docker proxy to nodes (#8359)

This commit is contained in:
2025-04-09 18:25:56 +08:00 committed by GitHub
parent 0934534ac5
commit 898be9e420
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 118 additions and 8 deletions

View file

@ -33,6 +33,8 @@ const (
OneDriveRedirectURI = "http://localhost/login/authorized"
GoogleRedirectURI = "http://localhost:8080"
DaemonJsonPath = "/etc/docker/daemon.json"
)
const (

View file

@ -1520,6 +1520,11 @@ const message = {
proxyDocker: 'Docker Proxy',
proxyDockerHelper:
'Synchronize proxy server configuration to Docker, support offline server image pulling and other operations',
syncToNode: 'Sync to Node',
syncToNodeHelper: 'Sync settings to other nodes',
nodes: 'Node',
selectNode: 'Select Node',
selectNodeError: 'Please select a node',
apiInterface: 'API Interface',
apiInterfaceClose: 'Once closed, API interfaces cannot be accessed. Do you want to continue?',
apiInterfaceHelper: 'Provide panel support for API interface access',

View file

@ -1461,6 +1461,11 @@ const message = {
proxyDocker: 'Dockerプロキシ',
proxyDockerHelper:
'プロキシサーバーの構成をDockerに同期しオフラインサーバーイメージの引っ張りやその他の操作をサポートします',
syncToNode: '子ノードに同期',
syncToNodeHelper: '選択したノードへの同期設定',
nodes: 'ノード',
selectNode: 'ノードを選択',
selectNodeError: 'ノードを選択してください',
apiInterface: 'APIを有効にします',
apiInterfaceClose: '閉じたらAPIインターフェイスにアクセスできません続けたいですか',
apiInterfaceHelper: 'サードパーティのアプリケーションにAPIにアクセスできるようにします',

View file

@ -1443,6 +1443,11 @@ const message = {
proxyDocker: 'Docker 프록시',
proxyDockerHelper:
'프록시 서버 구성을 Docker 동기화하여 오프라인 서버 이미지 가져오기 등의 작업을 지원합니다.',
syncToNode: '자식 노드로 동기화',
syncToNodeHelper: '다른 노드로 설정 동기화',
nodes: '노드',
selectNode: '노드 선택',
selectNodeError: '노드를 선택해 주세요',
apiInterface: 'API 활성화',
apiInterfaceClose: '비활성화하면 API 인터페이스에 접근할 없습니다. 계속하시겠습니까?',
apiInterfaceHelper: '서드파티 애플리케이션이 API 접근할 있도록 허용합니다.',

View file

@ -1505,6 +1505,11 @@ const message = {
proxyDocker: 'Proksi Docker',
proxyDockerHelper:
'Selaraskan konfigurasi pelayan proksi ke Docker, menyokong tarikan imej pelayan luar talian dan operasi lain',
syncToNode: 'Penyegerakan ke nod anak',
syncToNodeHelper: 'Penyegerakan tetapan ke nod lain',
nodes: 'Nod',
selectNode: 'Pilih nod',
selectNodeError: 'Sila pilih nod',
apiInterface: 'Aktifkan API',
apiInterfaceClose: 'Setelah ditutup, antara muka API tidak boleh diakses. Adakah anda mahu meneruskan?',
apiInterfaceHelper: 'Benarkan aplikasi pihak ketiga mengakses API.',

View file

@ -1490,6 +1490,11 @@ const message = {
proxyDocker: 'Proxy Docker',
proxyDockerHelper:
'Sincronize a configuração do servidor proxy com o Docker, suportando operações de puxar imagens de servidor offline e outras',
syncToNode: 'Sincronizar para o filho',
syncToNodeHelper: 'Sincronizar de alerta para outros nós',
nodes: 'Nós',
selectNode: 'Selecionar ',
selectNodeError: 'Por favor, selecione um ',
apiInterface: 'Habilitar API',
apiInterfaceClose: 'Uma vez fechado, as interfaces da API não poderão ser acessadas. Deseja continuar?',
apiInterfaceHelper: 'Permitir que aplicativos de terceiros acessem a API.',

View file

@ -1494,6 +1494,11 @@ const message = {
proxyDocker: 'Прокси Docker',
proxyDockerHelper:
'Синхронизировать конфигурацию прокси-сервера с Docker, поддержка офлайн загрузки образов и других операций',
syncToNode: 'Синхронизация с дочерним узлом',
syncToNodeHelper: 'Синхронизация настроек с другими узлами',
nodes: 'Узлы',
selectNode: 'Выберите узел',
selectNodeError: 'Пожалуйста, выберите узел',
apiInterface: 'Включить API',
apiInterfaceClose: 'После закрытия API-интерфейсы будут недоступны. Хотите продолжить?',
apiInterfaceHelper: 'Разрешить сторонним приложениям доступ к API.',

View file

@ -1439,6 +1439,11 @@ const message = {
proxyPasswdKeep: '記住密碼',
proxyDocker: 'Docker 代理',
proxyDockerHelper: '將代理伺服器配寘同步至 Docker支持離線服務器拉取鏡像等操作',
syncToNode: '同步至子節點',
syncToNodeHelper: '同步設置至其他節點',
nodes: '節點',
selectNode: '選擇節點',
selectNodeError: '請選擇節點',
apiInterface: 'API 接口',
apiInterfaceClose: '關閉後將不能使用 API 接口進行訪問是否繼續',
apiInterfaceHelper: '提供面板支持 API 接口訪問',

View file

@ -1435,6 +1435,11 @@ const message = {
proxyPasswdKeep: '记住密码',
proxyDocker: 'Docker 代理',
proxyDockerHelper: '将代理服务器配置同步至 Docker支持离线服务器拉取镜像等操作',
syncToNode: '同步至子节点',
syncToNodeHelper: '同步设置至所选节点',
nodes: '节点',
selectNode: '选择节点',
selectNodeError: '请选择节点',
apiInterface: 'API 接口',
apiInterfaceClose: '关闭后将不能使用 API 接口进行访问是否继续',
apiInterfaceHelper: '提供面板支持 API 接口访问',

View file

@ -139,7 +139,7 @@
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.proxy')" prop="proxyShow">
<el-form-item :label="$t('setting.proxy')" prop="proxyShow" v-if="isMaster">
<el-input disabled v-model="form.proxyShow">
<template #append>
<el-button @click="onChangeProxy" icon="Setting">
@ -229,7 +229,7 @@ const loading = ref(false);
const i18n = useI18n();
const globalStore = GlobalStore();
const { isMasterProductPro } = storeToRefs(globalStore);
const { isMasterProductPro, isMaster } = storeToRefs(globalStore);
const { switchTheme } = useTheme();
const mobile = computed(() => {
@ -266,6 +266,8 @@ const form = reactive({
proxyPasswd: '',
proxyPasswdKeep: '',
proxyDocker: '',
proxyDockerSyncToNode: '',
proxyDockerSyncNodes: '',
apiInterfaceStatus: 'Disable',
apiKey: '',
@ -339,6 +341,8 @@ const search = async () => {
: '{"light":"#005eeb","dark":"#F0BE96"}';
globalStore.themeConfig.theme = form.theme;
form.proxyDocker = xpackRes.data.proxyDocker;
form.proxyDockerSyncToNode = xpackRes.data.proxyDockerSyncToNode;
form.proxyDockerSyncNodes = xpackRes.data.proxyDockerSyncNodes;
}
} else {
globalStore.themeConfig.theme = form.theme;
@ -369,6 +373,8 @@ const onChangeProxy = () => {
passwd: form.proxyPasswd,
passwdKeep: form.proxyPasswdKeep,
proxyDocker: form.proxyDocker,
proxyDockerSyncToNode: form.proxyDockerSyncToNode,
proxyDockerSyncNodes: form.proxyDockerSyncNodes,
});
};

View file

@ -46,9 +46,41 @@
<el-checkbox v-model="form.proxyPasswdKeepItem" :label="$t('setting.proxyPasswdKeep')" />
</el-form-item>
<el-form-item v-if="isMasterProductPro">
<el-checkbox v-model="form.proxyDocker" :label="$t('setting.proxyDocker')" />
<el-checkbox v-model="form.proxyDocker" @change="onChange" :label="$t('setting.proxyDocker')" />
<span class="input-help">{{ $t('setting.proxyDockerHelper') }}</span>
</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>
</el-form>
<template #footer>
@ -70,13 +102,13 @@ import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus';
import { reactive, ref } from 'vue';
import { updateProxy } from '@/api/modules/setting';
import { listNodeOptions, updateProxy } from '@/api/modules/setting';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
import { updateXpackSettingByKey } from '@/utils/xpack';
import { updateDaemonJson } from '@/api/modules/container';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { escapeProxyURL } from '@/utils/util';
import { updateDockerProxySetting } from '@/xpack/api/modules/setting';
const globalStore = GlobalStore();
const emit = defineEmits<{ (e: 'search'): void }>();
@ -103,7 +135,11 @@ const form = reactive({
proxyPasswdKeep: '',
proxyPasswdKeepItem: false,
proxyDocker: false,
syncToNode: 'Enable',
selectNode: 'all',
nodes: [],
});
const nodeList = ref([]);
interface DialogProps {
url: string;
@ -113,6 +149,8 @@ interface DialogProps {
passwd: string;
passwdKeep: string;
proxyDocker: string;
proxyDockerSyncToNode: string;
proxyDockerSyncNodes: string;
}
const acceptParams = (params: DialogProps): void => {
if (params.url) {
@ -129,11 +167,24 @@ const acceptParams = (params: DialogProps): void => {
form.proxyUser = params.user;
form.proxyPasswd = params.passwd;
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 !== '';
passwordVisible.value = true;
form.proxyPasswdKeepItem = params.passwdKeep === 'Enable';
};
const getNodes = () => {
if (form.syncToNode === 'Enable' && nodeList.value.length === 0) {
listNodes();
}
};
const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
@ -216,16 +267,27 @@ const onSubmit = async () => {
if (isClose || params.proxyDocker === false) {
proxyUrl = '';
}
await updateXpackSettingByKey('ProxyDocker', proxyUrl);
await updateDaemonJson(`${form.proxyType}-proxy`, proxyUrl);
let param = {
proxyDocker: proxyUrl,
proxyDockerSyncToNode: form.syncToNode,
proxyDockerSyncNodes: JSON.stringify(form.nodes),
};
await updateDockerProxySetting(param);
emit('search');
handleClose();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
} catch {
} finally {
loading.value = false;
}
};
const listNodes = async () => {
try {
const res = await listNodeOptions();
nodeList.value = res.data || [];
} catch (error) {}
};
const handleClose = () => {
passwordVisible.value = false;
};