fix: 数据源编辑密码保留 + Redis ACL 认证支持

后端:
- UpdateInstance: 空密码和'******'都不更新密码字段,防止编辑后丢失密码
- testRedis/getRedisClient/getRedisMetrics: 增加 Username 参数支持 Redis 6.0+ ACL 认证
- username 为 'default' 或空时走传统密码认证模式

前端:
- EditInstanceDialog: Redis 用户名字段提示'6.0+ ACL 可选',密码 placeholder 改为'不修改请留空'
- CreateInstanceForm: Redis 类型也显示用户名输入框(可选),创建/测试连接时 Redis username 为空则不传
- Redis 提示文案从'无需用户名'改为'6.0+ ACL 支持用户名+密码认证'
This commit is contained in:
logan committed 2026-05-31 23:55:18 +08:00
1 parent bfb64d4dce
commit 8af868b3ca
5 files changed
+59 -34

No files matched your search

@@ -172,12 +172,15 @@ export default function EditInstanceDialog({ instance, open, onClose, onSaved, m
<div className="grid grid-cols-2 gap-3">
<div>
<label className={labelCls}>用户名</label>
<input className={inputCls} value={username} onChange={e => setUsername(e.target.value)} />
<label className={labelCls}>
用户名
{instance.db_type === 'redis' && <span className="font-normal text-[var(--dg-text-muted)]">(Redis 6.0+ ACL 可选)</span>}
</label>
<input className={inputCls} value={username} onChange={e => setUsername(e.target.value)} placeholder={instance.db_type === 'redis' ? '可选,留空则仅密码认证' : '用户名'} />
</div>
<div>
<label className={labelCls}>{mode === 'clone' ? '密码' : '密码(留空不修改)'}</label>
<input className={inputCls} type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="••••••" />
<input className={inputCls} type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder={mode === 'edit' ? '不修改请留空' : '••••••'} />
</div>
</div>
+21 -19
View File
@@ -1008,7 +1008,7 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const isRedis = dbType === 'redis';
const needUsername = !isRedis;
const needUsername = !isRedis; // Redis username is optional (6.0+ ACL)
const needDatabase = !isRedis;
const finalEnv = env === '__custom__' ? customEnv.trim() : env;
const canSubmit = testResult?.success === true;
@@ -1017,7 +1017,7 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
setDbType(t);
setPort(String(DB_DEFAULT_PORTS[t] || 3306));
setTestResult(null);
if (t === 'redis') { setUsername(''); setDatabase(''); }
if (t === 'redis') { setDatabase(''); }
}, []);
const handleSubmit = useCallback(async () => {
@@ -1028,7 +1028,8 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
try {
await instanceApi.create({
name, db_type: dbType, host, port: Number(port),
username: username || 'default', password: password || 'default',
username: isRedis ? (username || undefined) : (username || 'default'),
password: password || 'default',
database: database || undefined,
env: (finalEnv || 'development') as string,
access_mode: accessMode,
@@ -1054,7 +1055,8 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
try {
const res = await instanceApi.testNewConnection({
name: name || '__test__', db_type: dbType, host, port: Number(port),
username: username || 'default', password: password || 'default',
username: isRedis ? (username || undefined) : (username || 'default'),
password: password || 'default',
database: database || undefined,
env: (finalEnv || 'development') as string, access_mode: accessMode,
});
@@ -1207,10 +1209,10 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
</div>
</div>
{isRedis && (
<div className="p-3 rounded-lg bg-yellow-500/5 border border-yellow-500/20">
<p className="text-xs text-yellow-400 flex items-center gap-1.5">
<div className="p-3 rounded-lg bg-blue-500/5 border border-blue-500/20">
<p className="text-xs text-blue-400 flex items-center gap-1.5">
<AlertTriangle className="w-3.5 h-3.5" />
Redis 实例无需用户名,密码可选
Redis 6.0+ ACL 支持用户名+密码认证;传统模式仅填密码即可
</p>
</div>
)}
@@ -1223,7 +1225,7 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
<Card className="dg-card">
<CardContent className="p-5 space-y-4">
<h3 className="text-sm font-semibold text-[var(--dg-text-secondary)]">连接信息</h3>
<div className={isRedis ? 'grid grid-cols-2 gap-4' : 'grid grid-cols-3 gap-4'}>
<div className="grid grid-cols-3 gap-4">
<div>
<label className={labelCls}>地址 <span className="text-red-400">*</span></label>
<input
@@ -1242,17 +1244,17 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
placeholder={String(DB_DEFAULT_PORTS[dbType] || 3306)}
/>
</div>
{!isRedis && (
<div>
<label className={labelCls}>用户名 <span className="text-red-400">*</span></label>
<input
value={username}
onChange={e => { setUsername(e.target.value); setTestResult(null); }}
className={inputCls}
placeholder="root"
/>
</div>
)}
<div>
<label className={labelCls}>
用户名 {isRedis ? <span className="text-[var(--dg-text-muted)]">(可选)</span> : <span className="text-red-400">*</span>}
</label>
<input
value={username}
onChange={e => { setUsername(e.target.value); setTestResult(null); }}
className={inputCls}
placeholder={isRedis ? 'ACL 用户名,留空则仅密码认证' : 'root'}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>