feat(admin): add ConfigSync page + close ADMIN-01/02 (AUDIT_TRACKER)
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- ADMIN-01 FIXED: ConfigSync.tsx page with ProTable + pagination - config-sync service calling GET /config/sync-logs - route + nav item + breadcrumb - backend @reserved → @connected - ADMIN-02 FALSE_POSITIVE: Logs.tsx + logs service already exist
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
CrownOutlined,
|
||||
SafetyOutlined,
|
||||
FieldTimeOutlined,
|
||||
SyncOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Avatar, Dropdown, Tooltip, Drawer } from 'antd'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
@@ -51,6 +52,7 @@ const navItems: NavItem[] = [
|
||||
{ path: '/knowledge', name: '知识库', icon: <BookOutlined />, permission: 'knowledge:read', group: '资源管理' },
|
||||
{ path: '/billing', name: '计费管理', icon: <CrownOutlined />, permission: 'billing:read', group: '核心' },
|
||||
{ path: '/logs', name: '操作日志', icon: <FileTextOutlined />, permission: 'admin:full', group: '运维' },
|
||||
{ path: '/config-sync', name: '同步日志', icon: <SyncOutlined />, permission: 'config:read', group: '运维' },
|
||||
{ path: '/config', name: '系统配置', icon: <SettingOutlined />, permission: 'config:read', group: '系统' },
|
||||
{ path: '/prompts', name: '提示词管理', icon: <MessageOutlined />, permission: 'prompt:read', group: '系统' },
|
||||
]
|
||||
@@ -219,6 +221,7 @@ const breadcrumbMap: Record<string, string> = {
|
||||
'/config': '系统配置',
|
||||
'/prompts': '提示词管理',
|
||||
'/logs': '操作日志',
|
||||
'/config-sync': '同步日志',
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
111
admin-v2/src/pages/ConfigSync.tsx
Normal file
111
admin-v2/src/pages/ConfigSync.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
// ============================================================
|
||||
// 配置同步日志
|
||||
// ============================================================
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Tag, Typography } from 'antd'
|
||||
import type { ProColumns } from '@ant-design/pro-components'
|
||||
import { ProTable } from '@ant-design/pro-components'
|
||||
import { configSyncService } from '@/services/config-sync'
|
||||
import type { ConfigSyncLog } from '@/types'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
const actionLabels: Record<string, string> = {
|
||||
push: '推送',
|
||||
merge: '合并',
|
||||
pull: '拉取',
|
||||
diff: '差异',
|
||||
}
|
||||
|
||||
const actionColors: Record<string, string> = {
|
||||
push: 'blue',
|
||||
merge: 'green',
|
||||
pull: 'cyan',
|
||||
diff: 'orange',
|
||||
}
|
||||
|
||||
export default function ConfigSync() {
|
||||
const [page, setPage] = useState(1)
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['config-sync', page],
|
||||
queryFn: ({ signal }) => configSyncService.list({ page, page_size: 20 }, signal),
|
||||
})
|
||||
|
||||
const columns: ProColumns<ConfigSyncLog>[] = [
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 100,
|
||||
render: (_, r) => (
|
||||
<Tag color={actionColors[r.action] || 'default'}>
|
||||
{actionLabels[r.action] || r.action}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '客户端指纹',
|
||||
dataIndex: 'client_fingerprint',
|
||||
width: 160,
|
||||
render: (_, r) => <code>{r.client_fingerprint.substring(0, 16)}...</code>,
|
||||
},
|
||||
{
|
||||
title: '配置键',
|
||||
dataIndex: 'config_keys',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '客户端值',
|
||||
dataIndex: 'client_values',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
render: (_, r) => r.client_values || '-',
|
||||
},
|
||||
{
|
||||
title: '服务端值',
|
||||
dataIndex: 'saas_values',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
render: (_, r) => r.saas_values || '-',
|
||||
},
|
||||
{
|
||||
title: '解决方式',
|
||||
dataIndex: 'resolution',
|
||||
width: 120,
|
||||
render: (_, r) => r.resolution || '-',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
dataIndex: 'created_at',
|
||||
width: 180,
|
||||
render: (_, r) => new Date(r.created_at).toLocaleString('zh-CN'),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Title level={4} style={{ margin: 0 }}>配置同步日志</Title>
|
||||
</div>
|
||||
|
||||
<ProTable<ConfigSyncLog>
|
||||
columns={columns}
|
||||
dataSource={data?.items ?? []}
|
||||
loading={isLoading}
|
||||
rowKey="id"
|
||||
search={false}
|
||||
toolBarRender={false}
|
||||
pagination={{
|
||||
total: data?.total ?? 0,
|
||||
pageSize: 20,
|
||||
current: page,
|
||||
onChange: setPage,
|
||||
showSizeChanger: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -35,6 +35,7 @@ export const router = createBrowserRouter([
|
||||
{ path: 'config', lazy: () => import('@/pages/Config').then((m) => ({ Component: m.default })) },
|
||||
{ path: 'prompts', lazy: () => import('@/pages/Prompts').then((m) => ({ Component: m.default })) },
|
||||
{ path: 'logs', lazy: () => import('@/pages/Logs').then((m) => ({ Component: m.default })) },
|
||||
{ path: 'config-sync', lazy: () => import('@/pages/ConfigSync').then((m) => ({ Component: m.default })) },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
7
admin-v2/src/services/config-sync.ts
Normal file
7
admin-v2/src/services/config-sync.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import request, { withSignal } from './request'
|
||||
import type { ConfigSyncLog, PaginatedResponse } from '@/types'
|
||||
|
||||
export const configSyncService = {
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<ConfigSyncLog>>('/config/sync-logs', withSignal({ params }, signal)).then((r) => r.data),
|
||||
}
|
||||
@@ -324,3 +324,16 @@ export interface CreateTemplateRequest {
|
||||
description?: string
|
||||
permissions?: string[]
|
||||
}
|
||||
|
||||
/** 配置同步日志 */
|
||||
export interface ConfigSyncLog {
|
||||
id: number
|
||||
account_id: string
|
||||
client_fingerprint: string
|
||||
action: string
|
||||
config_keys: string
|
||||
client_values: string | null
|
||||
saas_values: string | null
|
||||
resolution: string | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user