/* Copyright (C) 2025 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import React, { useState, useEffect, useRef } from 'react'; import { Modal, Form, Input, InputNumber, Typography, Card, Space, Divider, Button, Banner, Tag, Collapse, TextArea, Switch, } from '@douyinfe/semi-ui'; import { FaCog, FaDocker, FaKey, FaTerminal, FaNetworkWired, FaExclamationTriangle, FaPlus, FaMinus } from 'react-icons/fa'; import { API, showError, showSuccess } from '../../../../helpers'; const { Text, Title } = Typography; const UpdateConfigModal = ({ visible, onCancel, deployment, onSuccess, t }) => { const formRef = useRef(null); const [loading, setLoading] = useState(false); const [envVars, setEnvVars] = useState([]); const [secretEnvVars, setSecretEnvVars] = useState([]); // Initialize form data when modal opens useEffect(() => { if (visible && deployment) { // Set initial form values based on deployment data const initialValues = { image_url: deployment.container_config?.image_url || '', traffic_port: deployment.container_config?.traffic_port || null, entrypoint: deployment.container_config?.entrypoint?.join(' ') || '', registry_username: '', registry_secret: '', command: '', }; if (formRef.current) { formRef.current.setValues(initialValues); } // Initialize environment variables const envVarsList = deployment.container_config?.env_variables ? Object.entries(deployment.container_config.env_variables).map(([key, value]) => ({ key, value: String(value) })) : []; setEnvVars(envVarsList); setSecretEnvVars([]); } }, [visible, deployment]); const handleUpdate = async () => { try { const formValues = formRef.current ? await formRef.current.validate() : {}; setLoading(true); // Prepare the update payload const payload = {}; if (formValues.image_url) payload.image_url = formValues.image_url; if (formValues.traffic_port) payload.traffic_port = formValues.traffic_port; if (formValues.registry_username) payload.registry_username = formValues.registry_username; if (formValues.registry_secret) payload.registry_secret = formValues.registry_secret; if (formValues.command) payload.command = formValues.command; // Process entrypoint if (formValues.entrypoint) { payload.entrypoint = formValues.entrypoint.split(' ').filter(cmd => cmd.trim()); } // Process environment variables if (envVars.length > 0) { payload.env_variables = envVars.reduce((acc, env) => { if (env.key && env.value !== undefined) { acc[env.key] = env.value; } return acc; }, {}); } // Process secret environment variables if (secretEnvVars.length > 0) { payload.secret_env_variables = secretEnvVars.reduce((acc, env) => { if (env.key && env.value !== undefined) { acc[env.key] = env.value; } return acc; }, {}); } const response = await API.put(`/api/deployments/${deployment.id}`, payload); if (response.data.success) { showSuccess(t('容器配置更新成功')); onSuccess?.(response.data.data); handleCancel(); } } catch (error) { showError(t('更新配置失败') + ': ' + (error.response?.data?.message || error.message)); } finally { setLoading(false); } }; const handleCancel = () => { if (formRef.current) { formRef.current.reset(); } setEnvVars([]); setSecretEnvVars([]); onCancel(); }; const addEnvVar = () => { setEnvVars([...envVars, { key: '', value: '' }]); }; const removeEnvVar = (index) => { const newEnvVars = envVars.filter((_, i) => i !== index); setEnvVars(newEnvVars); }; const updateEnvVar = (index, field, value) => { const newEnvVars = [...envVars]; newEnvVars[index][field] = value; setEnvVars(newEnvVars); }; const addSecretEnvVar = () => { setSecretEnvVars([...secretEnvVars, { key: '', value: '' }]); }; const removeSecretEnvVar = (index) => { const newSecretEnvVars = secretEnvVars.filter((_, i) => i !== index); setSecretEnvVars(newSecretEnvVars); }; const updateSecretEnvVar = (index, field, value) => { const newSecretEnvVars = [...secretEnvVars]; newSecretEnvVars[index][field] = value; setSecretEnvVars(newSecretEnvVars); }; return ( {t('更新容器配置')} } visible={visible} onCancel={handleCancel} onOk={handleUpdate} okText={t('更新配置')} cancelText={t('取消')} confirmLoading={loading} width={700} className="update-config-modal" >
{/* Container Info */}
{deployment?.container_name}
ID: {deployment?.id}
{deployment?.status}
{/* Warning Banner */} } title={t('重要提醒')} description={

{t('更新容器配置可能会导致容器重启,请确保在合适的时间进行此操作。')}

{t('某些配置更改可能需要几分钟才能生效。')}

} />
(formRef.current = api)} layout="vertical" > {/* Docker Configuration */} {t('Docker 配置')}
} itemKey="docker" >
{/* Network Configuration */} {t('网络配置')} } itemKey="network" > {/* Startup Configuration */} {t('启动配置')} } itemKey="startup" >
{/* Environment Variables */} {t('环境变量')} {envVars.length} } itemKey="env" >
{/* Regular Environment Variables */}
{t('普通环境变量')}
{envVars.map((envVar, index) => (
updateEnvVar(index, 'key', value)} style={{ flex: 1 }} /> = updateEnvVar(index, 'value', value)} style={{ flex: 2 }} />
))} {envVars.length === 0 && (
{t('暂无环境变量')}
)}
{/* Secret Environment Variables */}
{t('机密环境变量')} {t('加密存储')}
{secretEnvVars.map((envVar, index) => (
updateSecretEnvVar(index, 'key', value)} style={{ flex: 1 }} /> = updateSecretEnvVar(index, 'value', value)} style={{ flex: 2 }} />
))} {secretEnvVars.length === 0 && (
{t('暂无机密环境变量')}
)}
{/* Final Warning */}
{t('配置更新确认')}
{t('更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。')}
); }; export default UpdateConfigModal;