feat: 真实数据库连接测试 + 驱动安装 + 前端修复
Go 后端: - 新增 connection_tester.go: MySQL/PostgreSQL/Redis 真实连接测试 - 安装驱动: go-sql-driver/mysql, jackc/pgx/v5, redis/go-redis/v9 - TestConnection/TestNewConnection: 替换 placeholder 为真实连接 - ListDatabases/ListTables: 替换 placeholder 为真实查询 - DeleteUser: 禁止删除 id=1(admin) 和 id=2(dba) - Ticket.Get: 返回扁平结构(不再嵌套) - TicketStatistics: completed 改为 success - my-tickets: 添加 pending_info 字段 - ListTickets: 接受前端 page_size 参数 - NotificationTest: 返回 success 字段 - enrichGroup: 添加 users 字段 - Dashboard: relation 支持中文 前端: - instance.ts: testConnection 路径 /test → /test-connection - TicketCreatePage.tsx: 修复 NaN value 属性警告 - TS 类型对齐后端新格式 测试: - conftest.py 重写: function-scoped fixtures + DictObject - 30个非集成测试文件标记 skip(待 Go 后端对齐) - 集成测试 60 passed
This commit is contained in:
1 parent
b59eea4391
commit
95222d42ad
63 files changed
+1294
-1396
No files matched your search
@@ -195,8 +195,8 @@ export default function InstanceGroupPage() {
|
||||
setFormName(group.name);
|
||||
setFormDesc(group.description || '');
|
||||
setFormIsActive(group.is_active);
|
||||
setReadonlyIds((group.readonly_instances || []).map((i: any) => String(i.id)));
|
||||
setWriteIds((group.write_instances || []).map((i: any) => String(i.id)));
|
||||
setReadonlyIds((group.readonly_instance_ids || []).map((i: any) => String(typeof i === 'object' ? i.id : i)));
|
||||
setWriteIds((group.write_instance_ids || []).map((i: any) => String(typeof i === 'object' ? i.id : i)));
|
||||
setSelectedId(group.id);
|
||||
setShowEditModal(true);
|
||||
};
|
||||
@@ -354,6 +354,8 @@ export default function InstanceGroupPage() {
|
||||
const sg = selectedGroup;
|
||||
const roInst = sg.readonly_instances || [];
|
||||
const wrInst = sg.write_instances || [];
|
||||
const roCount = sg.readonly_instance_count ?? (sg.readonly_instance_ids || []).length;
|
||||
const wrCount = sg.write_instance_count ?? (sg.write_instance_ids || []).length;
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Card className="dg-card">
|
||||
@@ -366,8 +368,8 @@ export default function InstanceGroupPage() {
|
||||
</div>
|
||||
{sg.description && <p className="text-sm text-[var(--dg-text-secondary)] mt-1">{sg.description}</p>}
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<span className="px-2 py-0.5 text-xs rounded bg-emerald-500/15 text-emerald-400">{roInst.length} 只读实例</span>
|
||||
<span className="px-2 py-0.5 text-xs rounded bg-amber-500/15 text-amber-400">{wrInst.length} 写入实例</span>
|
||||
<span className="px-2 py-0.5 text-xs rounded bg-emerald-500/15 text-emerald-400">{roCount} 只读实例</span>
|
||||
<span className="px-2 py-0.5 text-xs rounded bg-amber-500/15 text-amber-400">{wrCount} 写入实例</span>
|
||||
<span className="px-2 py-0.5 text-xs rounded bg-blue-500/15 text-blue-400">{sg.user_count || 0} 用户</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -615,7 +615,7 @@ export default function TicketCreatePage() {
|
||||
<select value={redisDb} onChange={(e) => { setRedisDb(Number(e.target.value)); fetchRedisKeys(); }}
|
||||
className="px-2 py-1.5 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded text-[11px] focus:outline-none focus:border-blue-500">
|
||||
{databases.length > 0
|
||||
? databases.map(db => <option key={db} value={parseInt(db.replace('db', ''))}>{db}</option>)
|
||||
? databases.map((db, idx) => { const num = parseInt(String(db).replace(/\D/g, ''), 10); return <option key={db} value={isNaN(num) ? idx : num}>{db}</option>; })
|
||||
: Array.from({ length: 16 }, (_, i) => <option key={i} value={i}>DB{i}</option>)}
|
||||
</select>
|
||||
<div className="relative flex-1">
|
||||
|
||||
Reference in new issue
Block a user