Skip to content

Commit 0838a2d

Browse files
authored
Feat/irify/approve some UI ux (#16)
* feat: add scan observability workspace ui * ux(ssa): simplify project scan entry * feat: formalize irify report center * fix: prevent duplicate project rows during scan launch * refactor(ux): p0+p1 frontend interaction optimizations remove fake dashboard trends, consolidate 10 API calls into single Promise.all, add @tanstack/react-query with QueryClientProvider (staleTime 30s), create useUrlState hook for URL filter persistence, migrate dashboard and vulnerability list filter options to useQuery, persist filter state in URL for vulnerability list (6 filters) and project management (4 filters) * fix: add missing import and extract nested component in scan observability page * fix: clarify delete and cancel interactions * refactor(style): migrate scan observability page to irify design tokens Replace all --obs-* local CSS variables and hardcoded hex colors with var(--irify-*) global tokens for automatic dark/light theme support. Add useTheme() hook and isDark className pattern to match other IRify pages. * refactor(style): redesign scan observability page to match irify dashboard aesthetic
2 parents 9dcba3c + 4b00453 commit 0838a2d

File tree

30 files changed

+3777
-467
lines changed

30 files changed

+3777
-467
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@dnd-kit/sortable": "^10.0.0",
3434
"@dnd-kit/utilities": "^3.2.2",
3535
"@hello-pangea/dnd": "16.5.0",
36+
"@tanstack/react-query": "^5.95.0",
3637
"@uiw/react-md-editor": "3.6.5",
3738
"@viz-js/viz": "3.7.0",
3839
"@yakit-libs/color": "^1.0.2",

pnpm-lock.yaml

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App/IRifyLayout.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ const collapsibleNavItems = [
9999
label: '节点管理',
100100
path: '/node-config/manage',
101101
},
102+
{
103+
key: '/node-config/observability',
104+
label: '扫描观测',
105+
path: '/node-config/observability',
106+
},
102107
],
103108
},
104109
{
@@ -202,6 +207,8 @@ const IRifyLayout: React.FC = () => {
202207
return ['节点配置', '节点安装'];
203208
if (path.startsWith('/node-config/manage'))
204209
return ['节点配置', '节点管理'];
210+
if (path.startsWith('/node-config/observability'))
211+
return ['节点配置', '扫描观测'];
205212

206213
if (path.startsWith('/system-management/userinfo'))
207214
return ['系统管理', '用户管理'];

src/App/routers/irify-routers.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import { RuleEditor } from '@/pages/RuleManagement/RuleEditor';
1616
import SSARiskAudit from '@/pages/SSARiskAudit';
1717
import TaskList from '@/pages/SSAScanTask/TaskList';
1818
import IRifyNodeManagePage from '@/pages/NodeManage/IRifyNodeManagePage';
19+
import IRifyScanObservabilityPage from '@/pages/NodeManage/IRifyScanObservabilityPage';
1920
import IRifySystemManagementPage from '@/pages/SystemManagement/IRifySystemManagementPage';
20-
import ReportManage from '@/pages/ReportManage';
21+
import IRifyReportManagePage from '@/pages/IRifyReportManage';
2122
import CompileArtifactsPage from '@/pages/CompileArtifacts/CompileArtifactsPage';
2223

2324
import IRifyDashboard from '@/pages/IRifyDashboard';
@@ -110,7 +111,7 @@ const irifyRouters: RouteObject[] = [
110111
},
111112
{
112113
path: 'reports',
113-
element: <ReportManage />,
114+
element: <IRifyReportManagePage />,
114115
},
115116
{
116117
path: 'node-config',
@@ -123,6 +124,10 @@ const irifyRouters: RouteObject[] = [
123124
path: 'manage',
124125
element: <IRifyNodeManagePage />,
125126
},
127+
{
128+
path: 'observability',
129+
element: <IRifyScanObservabilityPage />,
130+
},
126131
],
127132
},
128133
{

src/apis/NodeManageApi/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { ResponseData, TableResponseData } from '@/utils/commonTypes';
33
import type {
44
PostHostAliveDetectionRunRequest,
55
QueryPalmNodeParams,
6+
ScannerObservabilityOverview,
67
TPostNodesDownloadDataRunRequest,
78
} from './type';
89
import type { Palm } from '@/gen/schema';
@@ -53,10 +54,40 @@ const postHostAliveDetectionRun = (data: {
5354
ResponseData<TableResponseData<PostHostAliveDetectionRunRequest>>
5455
>('/task/start/host-alive-detection/run', data);
5556

57+
const getScannerObservabilityOverview = (params?: {
58+
task_limit?: number;
59+
}): Promise<ResponseData<ScannerObservabilityOverview>> =>
60+
axios.get<never, ResponseData<ScannerObservabilityOverview>>(
61+
'/ssa/observability/scanner/overview',
62+
{
63+
params,
64+
},
65+
);
66+
67+
const exportScannerObservabilityDiagnostics = (params?: {
68+
task_limit?: number;
69+
log_limit?: number;
70+
node_id?: string;
71+
task_id?: string;
72+
}): Promise<ResponseData<Blob>> =>
73+
axios.get('/ssa/observability/scanner/diagnostics/export', {
74+
params,
75+
responseType: 'blob',
76+
transformResponse: [
77+
(data) => ({
78+
data,
79+
code: 200,
80+
msg: '',
81+
}),
82+
],
83+
});
84+
5685
export {
5786
getNodeManage,
5887
postUpdateLocation,
5988
postNodesDownloadDataRun,
6089
deleteNodeManage,
6190
postHostAliveDetectionRun,
91+
getScannerObservabilityOverview,
92+
exportScannerObservabilityDiagnostics,
6293
};

src/apis/NodeManageApi/type.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,87 @@ interface PostHostAliveDetectionRunRequest {
2929
result: Array<string>;
3030
}
3131

32+
interface ScannerObservabilityRunningTask {
33+
node_id: string;
34+
task_id: string;
35+
root_task_id?: string;
36+
sub_task_id?: string;
37+
runtime_id?: string;
38+
type: string;
39+
status: string;
40+
wait_ms: number;
41+
start_timestamp: number;
42+
running_timestamp: number;
43+
ddl_timestamp: number;
44+
elapsed_ms: number;
45+
}
46+
47+
interface ScannerObservabilityNode {
48+
node_id: string;
49+
nickname: string;
50+
location: string;
51+
external_ip: string;
52+
main_addr: string;
53+
last_seen_at: number;
54+
online: boolean;
55+
cpu_percent: number;
56+
memory_percent: number;
57+
network_upload: number;
58+
network_download: number;
59+
active_count: number;
60+
queue_count: number;
61+
capacity: number;
62+
recent_avg_wait_ms: number;
63+
recent_avg_exec_ms: number;
64+
recent_completed_count: number;
65+
rpc_error?: string;
66+
running_tasks: ScannerObservabilityRunningTask[];
67+
}
68+
69+
interface ScannerObservabilityRecentTask {
70+
task_id: string;
71+
project_name: string;
72+
scan_batch: number;
73+
execute_node: string;
74+
status: string;
75+
phase: string;
76+
progress: number;
77+
language: string;
78+
source_origin: string;
79+
error_message?: string;
80+
created_at: number;
81+
started_at?: number;
82+
finished_at?: number;
83+
}
84+
85+
interface ScannerObservabilitySummary {
86+
total_nodes: number;
87+
online_nodes: number;
88+
offline_nodes: number;
89+
total_capacity: number;
90+
total_active: number;
91+
total_queued: number;
92+
recent_avg_wait_ms: number;
93+
recent_avg_exec_ms: number;
94+
recent_completed_count: number;
95+
}
96+
97+
interface ScannerObservabilityOverview {
98+
generated_at: number;
99+
summary: ScannerObservabilitySummary;
100+
nodes: ScannerObservabilityNode[];
101+
running_tasks: ScannerObservabilityRunningTask[];
102+
recent_tasks: ScannerObservabilityRecentTask[];
103+
}
104+
32105
export type {
33106
QueryPalmNodeParams,
34107
TPostNodesDownloadDataRunRequest,
35108
NetworkPingTableProp,
36109
PostHostAliveDetectionRunRequest,
110+
ScannerObservabilityRunningTask,
111+
ScannerObservabilityNode,
112+
ScannerObservabilityRecentTask,
113+
ScannerObservabilitySummary,
114+
ScannerObservabilityOverview,
37115
};

src/apis/SSAProjectApi/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const postSSAProject = (
4444
// DELETE /ssa/project - 删除项目
4545
const deleteSSAProject = (params: {
4646
id: number;
47+
delete_mode?: 'config-only' | 'cascade';
4748
}): Promise<ResponseData<boolean>> =>
4849
axios.delete<never, ResponseData<boolean>>(`/ssa/project`, { params });
4950

@@ -69,9 +70,12 @@ const testGitConnection = (data: {
6970
const getScanPolicyConfig = (
7071
signal?: AbortSignal,
7172
): Promise<ResponseData<TScanPolicyConfig>> =>
72-
axios.get<never, ResponseData<TScanPolicyConfig>>(`/ssa/scan-policy-config`, {
73-
signal,
74-
});
73+
axios.get<never, ResponseData<TScanPolicyConfig>>(
74+
`/ssa/scan-policy-config`,
75+
{
76+
signal,
77+
},
78+
);
7579

7680
export {
7781
getSSAProjects,

src/apis/SSAProjectApi/type.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ interface TSSAProjectRequest {
8383
// SSA 项目列表响应类型
8484
type TSSAProjectListResponse = TableResponseData<TSSAProject>;
8585

86+
export type TSSAProjectDeleteMode = 'config-only' | 'cascade';
87+
8688
// 扫描策略配置类型
8789
export interface TScanPolicyConfig {
8890
version?: string;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import axios from '@/utils/axios';
2+
import type { ResponseData } from '@/utils/commonTypes';
3+
import type {
4+
TSSAReportRecord,
5+
TSSAReportRecordCreateRequest,
6+
TSSAReportRecordDetail,
7+
TSSAReportRecordListAPIResponse,
8+
TSSAReportRecordListResponse,
9+
TSSAReportRecordQueryParams,
10+
} from './type';
11+
12+
const normalizeListResponse = (
13+
payload?: TSSAReportRecordListAPIResponse['data'],
14+
): TSSAReportRecordListResponse => ({
15+
list: payload?.data || [],
16+
pagemeta: {
17+
page: payload?.paging?.pagemeta?.page || 1,
18+
limit: payload?.paging?.pagemeta?.limit || 20,
19+
total: payload?.paging?.pagemeta?.total || 0,
20+
total_page: payload?.paging?.pagemeta?.total_page || 1,
21+
},
22+
});
23+
24+
export const querySSAReportRecords = async (
25+
params?: TSSAReportRecordQueryParams,
26+
): Promise<ResponseData<TSSAReportRecordListResponse>> => {
27+
const res = await axios.get<never, TSSAReportRecordListAPIResponse>(
28+
'/ssa/report-records',
29+
{
30+
params,
31+
},
32+
);
33+
return {
34+
...res,
35+
data: normalizeListResponse(res.data),
36+
};
37+
};
38+
39+
export const createSSAReportRecord = (
40+
data: TSSAReportRecordCreateRequest,
41+
): Promise<ResponseData<TSSAReportRecord>> =>
42+
axios.post<never, ResponseData<TSSAReportRecord>>(
43+
'/ssa/report-records',
44+
data,
45+
);
46+
47+
export const fetchSSAReportRecord = (
48+
id: number,
49+
): Promise<ResponseData<TSSAReportRecordDetail>> =>
50+
axios.get<never, ResponseData<TSSAReportRecordDetail>>(
51+
`/ssa/report-records/${id}`,
52+
);
53+
54+
export const deleteSSAReportRecord = (
55+
id: number,
56+
): Promise<ResponseData<boolean>> =>
57+
axios.delete<never, ResponseData<boolean>>(`/ssa/report-records/${id}`);

0 commit comments

Comments
 (0)