UX优化: 同步日志显示服务器名 + redis_url可编辑
- 同步日志API: JOIN Server表返回server_name字段 - 推送历史: 显示服务器名称替代操作人 - 仪表盘最近同步: 显示服务器名称替代server_id - redis_url从SENSITIVE_KEYS移除(非密码,用户需可编辑) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -282,10 +282,11 @@ async def get_all_sync_logs(
|
||||
):
|
||||
"""Get recent sync logs across all servers (for dashboard charts)"""
|
||||
result = await db.execute(
|
||||
select(SyncLog).order_by(SyncLog.started_at.desc()).limit(limit)
|
||||
select(SyncLog, Server.name).join(Server, SyncLog.server_id == Server.id, isouter=True)
|
||||
.order_by(SyncLog.started_at.desc()).limit(limit)
|
||||
)
|
||||
logs = result.scalars().all()
|
||||
return [_sync_log_to_dict(log) for log in logs]
|
||||
rows = result.all()
|
||||
return [_sync_log_to_dict(log, server_name=name) for log, name in rows]
|
||||
|
||||
|
||||
@router.get("/{id}/logs", response_model=list)
|
||||
@@ -330,11 +331,12 @@ def _server_to_dict(server: Server) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _sync_log_to_dict(log) -> dict:
|
||||
def _sync_log_to_dict(log, server_name: str = None) -> dict:
|
||||
"""Convert SyncLog model to API response dict"""
|
||||
return {
|
||||
"id": log.id,
|
||||
"server_id": log.server_id,
|
||||
"server_name": server_name,
|
||||
"source_path": log.source_path,
|
||||
"target_path": log.target_path,
|
||||
"trigger_type": log.trigger_type,
|
||||
|
||||
@@ -24,7 +24,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
IMMUTABLE_KEYS = {"secret_key", "api_key", "encryption_key", "database_url"}
|
||||
|
||||
# Settings whose values are masked in GET responses
|
||||
SENSITIVE_KEYS = {"secret_key", "api_key", "encryption_key", "redis_url"}
|
||||
SENSITIVE_KEYS = {"secret_key", "api_key", "encryption_key"}
|
||||
|
||||
router = APIRouter(prefix="/api/settings", tags=["settings"])
|
||||
|
||||
|
||||
+1
-1
@@ -160,7 +160,7 @@
|
||||
const sc = l.status==='success'?'text-green-400':l.status==='failed'?'text-red-400':'text-yellow-400';
|
||||
const icon = l.status==='success'?'✅':l.status==='failed'?'❌':'⏳';
|
||||
return `<div class="flex items-center justify-between py-2 border-b border-slate-800 last:border-0">
|
||||
<div class="flex items-center gap-2"><span class="text-xs">${icon}</span><span class="text-slate-300 text-xs">${esc(l.sync_mode||'sync')} #${l.server_id||'?'}</span><span class="${sc} text-xs">${esc(l.status)}</span></div>
|
||||
<div class="flex items-center gap-2"><span class="text-xs">${icon}</span><span class="text-slate-300 text-xs">${esc(l.server_name||'#'+l.server_id)}</span><span class="${sc} text-xs">${esc(l.status)}</span></div>
|
||||
<span class="text-slate-600 text-xs">${fmtTime(l.started_at)}</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
+3
-3
@@ -213,11 +213,11 @@
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="${l.status==='success'?'text-green-400':'text-red-400'} text-sm">${l.status==='success'?'✓':'✗'}</span>
|
||||
<div>
|
||||
<span class="text-sm text-slate-300">${esc(l.operator||'system')}</span>
|
||||
<span class="text-sm text-slate-300">${esc(l.server_name||'#'+l.server_id)}</span>
|
||||
<span class="text-slate-600 mx-1">·</span>
|
||||
<span class="text-xs text-slate-500">${esc(l.operator||'system')}</span>
|
||||
<span class="text-slate-600 mx-1">·</span>
|
||||
<span class="text-xs text-slate-500">${esc(l.sync_mode||'file')}</span>
|
||||
<span class="text-slate-600 mx-1">·</span>
|
||||
<span class="text-xs text-slate-500">${esc(l.source_path||'')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-slate-500">${fmtTime(l.started_at)}</div>
|
||||
|
||||
Reference in New Issue
Block a user