@boarteam/boar-pack-users-frontend 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +38 -0
- package/src/.umi/core/EmptyRoute.tsx +9 -0
- package/src/.umi/core/defineApp.ts +17 -0
- package/src/.umi/core/helmet.ts +10 -0
- package/src/.umi/core/helmetContext.ts +4 -0
- package/src/.umi/core/history.ts +66 -0
- package/src/.umi/core/historyIntelli.ts +132 -0
- package/src/.umi/core/plugin.ts +55 -0
- package/src/.umi/core/pluginConfig.ts +299 -0
- package/src/.umi/core/pluginConfigJoi.d.ts +7 -0
- package/src/.umi/core/polyfill.ts +220 -0
- package/src/.umi/core/route.tsx +29 -0
- package/src/.umi/core/routeProps.ts +6 -0
- package/src/.umi/core/terminal.ts +37 -0
- package/src/.umi/exports.ts +23 -0
- package/src/.umi/plugin-access/context.ts +7 -0
- package/src/.umi/plugin-access/index.tsx +87 -0
- package/src/.umi/plugin-access/runtime.tsx +23 -0
- package/src/.umi/plugin-access/types.d.ts +5 -0
- package/src/.umi/plugin-locale/SelectLang.tsx +390 -0
- package/src/.umi/plugin-locale/index.ts +5 -0
- package/src/.umi/plugin-locale/locale.tsx +52 -0
- package/src/.umi/plugin-locale/localeExports.ts +279 -0
- package/src/.umi/plugin-locale/runtime.tsx +9 -0
- package/src/.umi/plugin-locale/runtimeConfig.d.ts +13 -0
- package/src/.umi/plugin-model/index.tsx +183 -0
- package/src/.umi/plugin-model/model.ts +6 -0
- package/src/.umi/plugin-model/runtime.tsx +20 -0
- package/src/.umi/testBrowser.tsx +87 -0
- package/src/.umi/tsconfig.json +44 -0
- package/src/.umi/typings.d.ts +136 -0
- package/src/.umi/umi.ts +76 -0
- package/src/access.ts +5 -0
- package/src/components/EventLogs/EventLogExplanation.tsx +166 -0
- package/src/components/EventLogs/EventLogsTable.tsx +146 -0
- package/src/components/EventLogs/EventLogsTimeline.tsx +109 -0
- package/src/components/EventLogs/UserAgentDisplay.tsx +21 -0
- package/src/components/EventLogs/eventLogsSearchableColumns.ts +87 -0
- package/src/components/EventLogs/getBrowserIcon.ts +27 -0
- package/src/components/EventLogs/useEventLogsColumns.tsx +375 -0
- package/src/components/index.tsx +7 -0
- package/src/index.tsx +1 -0
- package/src/pages/EventLogs/index.tsx +26 -0
- package/src/pages/getInitialState.tsx +114 -0
- package/src/tools/action.enum.ts +7 -0
- package/src/tools/api-client/apiClient.ts +6 -0
- package/src/tools/api-client/generated/ApiClient.ts +38 -0
- package/src/tools/api-client/generated/core/ApiError.ts +25 -0
- package/src/tools/api-client/generated/core/ApiRequestOptions.ts +17 -0
- package/src/tools/api-client/generated/core/ApiResult.ts +11 -0
- package/src/tools/api-client/generated/core/BaseHttpRequest.ts +14 -0
- package/src/tools/api-client/generated/core/CancelablePromise.ts +131 -0
- package/src/tools/api-client/generated/core/NodeHttpRequest.ts +26 -0
- package/src/tools/api-client/generated/core/OpenAPI.ts +32 -0
- package/src/tools/api-client/generated/core/request.ts +320 -0
- package/src/tools/api-client/generated/index.ts +26 -0
- package/src/tools/api-client/generated/models/EventLog.ts +56 -0
- package/src/tools/api-client/generated/models/EventLogCreateDto.ts +39 -0
- package/src/tools/api-client/generated/models/EventLogTimelineDto.ts +24 -0
- package/src/tools/api-client/generated/models/EventLogTimelineQueryDto.ts +11 -0
- package/src/tools/api-client/generated/models/EventLogUpdateDto.ts +39 -0
- package/src/tools/api-client/generated/models/GetManyEventLogResponseDto.ts +15 -0
- package/src/tools/api-client/generated/models/GetManyUserResponseDto.ts +15 -0
- package/src/tools/api-client/generated/models/PermissionDto.ts +9 -0
- package/src/tools/api-client/generated/models/User.ts +27 -0
- package/src/tools/api-client/generated/models/UserCreateDto.ts +23 -0
- package/src/tools/api-client/generated/models/UserUpdateDto.ts +23 -0
- package/src/tools/api-client/generated/services/EventLogsService.ts +178 -0
- package/src/tools/api-client/generated/services/UsersService.ts +226 -0
- package/src/tools/api-client/index.ts +2 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// This file is generated by Umi automatically
|
|
2
|
+
// DO NOT CHANGE IT MANUALLY!
|
|
3
|
+
type CSSModuleClasses = { readonly [key: string]: string }
|
|
4
|
+
declare module '*.css' {
|
|
5
|
+
const classes: CSSModuleClasses
|
|
6
|
+
export default classes
|
|
7
|
+
}
|
|
8
|
+
declare module '*.scss' {
|
|
9
|
+
const classes: CSSModuleClasses
|
|
10
|
+
export default classes
|
|
11
|
+
}
|
|
12
|
+
declare module '*.sass' {
|
|
13
|
+
const classes: CSSModuleClasses
|
|
14
|
+
export default classes
|
|
15
|
+
}
|
|
16
|
+
declare module '*.less' {
|
|
17
|
+
const classes: CSSModuleClasses
|
|
18
|
+
export default classes
|
|
19
|
+
}
|
|
20
|
+
declare module '*.styl' {
|
|
21
|
+
const classes: CSSModuleClasses
|
|
22
|
+
export default classes
|
|
23
|
+
}
|
|
24
|
+
declare module '*.stylus' {
|
|
25
|
+
const classes: CSSModuleClasses
|
|
26
|
+
export default classes
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// images
|
|
30
|
+
declare module '*.jpg' {
|
|
31
|
+
const src: string
|
|
32
|
+
export default src
|
|
33
|
+
}
|
|
34
|
+
declare module '*.jpeg' {
|
|
35
|
+
const src: string
|
|
36
|
+
export default src
|
|
37
|
+
}
|
|
38
|
+
declare module '*.png' {
|
|
39
|
+
const src: string
|
|
40
|
+
export default src
|
|
41
|
+
}
|
|
42
|
+
declare module '*.gif' {
|
|
43
|
+
const src: string
|
|
44
|
+
export default src
|
|
45
|
+
}
|
|
46
|
+
declare module '*.svg' {
|
|
47
|
+
import * as React from 'react';
|
|
48
|
+
export const ReactComponent: React.FunctionComponent<React.SVGProps<
|
|
49
|
+
SVGSVGElement
|
|
50
|
+
> & { title?: string }>;
|
|
51
|
+
|
|
52
|
+
const src: string
|
|
53
|
+
export default src
|
|
54
|
+
}
|
|
55
|
+
declare module '*.ico' {
|
|
56
|
+
const src: string
|
|
57
|
+
export default src
|
|
58
|
+
}
|
|
59
|
+
declare module '*.webp' {
|
|
60
|
+
const src: string
|
|
61
|
+
export default src
|
|
62
|
+
}
|
|
63
|
+
declare module '*.avif' {
|
|
64
|
+
const src: string
|
|
65
|
+
export default src
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// media
|
|
69
|
+
declare module '*.mp4' {
|
|
70
|
+
const src: string
|
|
71
|
+
export default src
|
|
72
|
+
}
|
|
73
|
+
declare module '*.webm' {
|
|
74
|
+
const src: string
|
|
75
|
+
export default src
|
|
76
|
+
}
|
|
77
|
+
declare module '*.ogg' {
|
|
78
|
+
const src: string
|
|
79
|
+
export default src
|
|
80
|
+
}
|
|
81
|
+
declare module '*.mp3' {
|
|
82
|
+
const src: string
|
|
83
|
+
export default src
|
|
84
|
+
}
|
|
85
|
+
declare module '*.wav' {
|
|
86
|
+
const src: string
|
|
87
|
+
export default src
|
|
88
|
+
}
|
|
89
|
+
declare module '*.flac' {
|
|
90
|
+
const src: string
|
|
91
|
+
export default src
|
|
92
|
+
}
|
|
93
|
+
declare module '*.aac' {
|
|
94
|
+
const src: string
|
|
95
|
+
export default src
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// fonts
|
|
99
|
+
declare module '*.woff' {
|
|
100
|
+
const src: string
|
|
101
|
+
export default src
|
|
102
|
+
}
|
|
103
|
+
declare module '*.woff2' {
|
|
104
|
+
const src: string
|
|
105
|
+
export default src
|
|
106
|
+
}
|
|
107
|
+
declare module '*.eot' {
|
|
108
|
+
const src: string
|
|
109
|
+
export default src
|
|
110
|
+
}
|
|
111
|
+
declare module '*.ttf' {
|
|
112
|
+
const src: string
|
|
113
|
+
export default src
|
|
114
|
+
}
|
|
115
|
+
declare module '*.otf' {
|
|
116
|
+
const src: string
|
|
117
|
+
export default src
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// other
|
|
121
|
+
declare module '*.wasm' {
|
|
122
|
+
const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>
|
|
123
|
+
export default initWasm
|
|
124
|
+
}
|
|
125
|
+
declare module '*.webmanifest' {
|
|
126
|
+
const src: string
|
|
127
|
+
export default src
|
|
128
|
+
}
|
|
129
|
+
declare module '*.pdf' {
|
|
130
|
+
const src: string
|
|
131
|
+
export default src
|
|
132
|
+
}
|
|
133
|
+
declare module '*.txt' {
|
|
134
|
+
const src: string
|
|
135
|
+
export default src
|
|
136
|
+
}
|
package/src/.umi/umi.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// This file is generated by Umi automatically
|
|
3
|
+
// DO NOT CHANGE IT MANUALLY!
|
|
4
|
+
import './core/polyfill';
|
|
5
|
+
|
|
6
|
+
import { renderClient } from '/Users/boarteam/.config/yarn/global/node_modules/@umijs/renderer-react';
|
|
7
|
+
import { getRoutes } from './core/route';
|
|
8
|
+
import { createPluginManager } from './core/plugin';
|
|
9
|
+
import { createHistory } from './core/history';
|
|
10
|
+
import { ApplyPluginsType } from 'umi';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const publicPath = "/";
|
|
14
|
+
const runtimePublicPath = false;
|
|
15
|
+
|
|
16
|
+
async function render() {
|
|
17
|
+
const pluginManager = createPluginManager();
|
|
18
|
+
const { routes, routeComponents } = await getRoutes(pluginManager);
|
|
19
|
+
|
|
20
|
+
// allow user to extend routes
|
|
21
|
+
await pluginManager.applyPlugins({
|
|
22
|
+
key: 'patchRoutes',
|
|
23
|
+
type: ApplyPluginsType.event,
|
|
24
|
+
args: {
|
|
25
|
+
routes,
|
|
26
|
+
routeComponents,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const contextOpts = pluginManager.applyPlugins({
|
|
31
|
+
key: 'modifyContextOpts',
|
|
32
|
+
type: ApplyPluginsType.modify,
|
|
33
|
+
initialValue: {},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const basename = contextOpts.basename || '/';
|
|
37
|
+
const historyType = contextOpts.historyType || 'browser';
|
|
38
|
+
|
|
39
|
+
const history = createHistory({
|
|
40
|
+
type: historyType,
|
|
41
|
+
basename,
|
|
42
|
+
...contextOpts.historyOpts,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return (pluginManager.applyPlugins({
|
|
46
|
+
key: 'render',
|
|
47
|
+
type: ApplyPluginsType.compose,
|
|
48
|
+
initialValue() {
|
|
49
|
+
const context = {
|
|
50
|
+
routes,
|
|
51
|
+
routeComponents,
|
|
52
|
+
pluginManager,
|
|
53
|
+
rootElement: contextOpts.rootElement || document.getElementById('root'),
|
|
54
|
+
publicPath,
|
|
55
|
+
runtimePublicPath,
|
|
56
|
+
history,
|
|
57
|
+
historyType,
|
|
58
|
+
basename,
|
|
59
|
+
callback: contextOpts.callback,
|
|
60
|
+
};
|
|
61
|
+
const modifiedContext = pluginManager.applyPlugins({
|
|
62
|
+
key: 'modifyClientRenderOpts',
|
|
63
|
+
type: ApplyPluginsType.modify,
|
|
64
|
+
initialValue: context,
|
|
65
|
+
});
|
|
66
|
+
return renderClient(modifiedContext);
|
|
67
|
+
},
|
|
68
|
+
}))();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
render();
|
|
73
|
+
|
|
74
|
+
window.g_umi = {
|
|
75
|
+
version: '4.1.10',
|
|
76
|
+
};
|
package/src/access.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { EventLog } from "@@api/generated";
|
|
3
|
+
import { Card, Descriptions, Tag, Typography } from "antd";
|
|
4
|
+
import {
|
|
5
|
+
ClockCircleOutlined,
|
|
6
|
+
CrownOutlined,
|
|
7
|
+
ExclamationCircleOutlined,
|
|
8
|
+
InfoCircleOutlined,
|
|
9
|
+
QuestionCircleOutlined,
|
|
10
|
+
SettingOutlined,
|
|
11
|
+
UserOutlined,
|
|
12
|
+
WarningOutlined
|
|
13
|
+
} from "@ant-design/icons";
|
|
14
|
+
import UserAgentDisplay from "./UserAgentDisplay";
|
|
15
|
+
|
|
16
|
+
const { Paragraph, Text, Title } = Typography;
|
|
17
|
+
|
|
18
|
+
function formatJson(s: string): string {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.stringify(JSON.parse(s), null, 2);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return s;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getUserRoleIcon(role: string): React.ReactNode {
|
|
27
|
+
switch (role) {
|
|
28
|
+
case EventLog.userRole.ADMIN:
|
|
29
|
+
return <CrownOutlined />;
|
|
30
|
+
case EventLog.userRole.USER:
|
|
31
|
+
return <UserOutlined />;
|
|
32
|
+
case EventLog.userRole.GUEST:
|
|
33
|
+
return <QuestionCircleOutlined />;
|
|
34
|
+
case EventLog.userRole.SYSTEM:
|
|
35
|
+
return <SettingOutlined />;
|
|
36
|
+
default:
|
|
37
|
+
return <QuestionCircleOutlined />;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getLogTypeTag(logType: string): React.ReactNode {
|
|
42
|
+
switch (logType) {
|
|
43
|
+
case EventLog.logType.AUDIT:
|
|
44
|
+
return <Tag icon={<UserOutlined />}>Audit Log</Tag>;
|
|
45
|
+
case EventLog.logType.OPERATIONAL:
|
|
46
|
+
return <Tag icon={<SettingOutlined />}>Operational Log</Tag>;
|
|
47
|
+
case EventLog.logType.APPLICATION:
|
|
48
|
+
return <Tag>Application Log</Tag>;
|
|
49
|
+
default:
|
|
50
|
+
return <Tag>Unknown</Tag>;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function renderUserAgentText(userAgent: string | null): React.ReactNode {
|
|
55
|
+
return userAgent
|
|
56
|
+
? <>using <UserAgentDisplay userAgent={userAgent} /> with User-Agent header:
|
|
57
|
+
<pre>{userAgent}</pre>
|
|
58
|
+
</>
|
|
59
|
+
: 'without User-Agent header';
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const EventLogExplanation: React.FC<{ record: EventLog }> = ({ record }) => {
|
|
64
|
+
const duration = record.duration !== null ? <Tag icon={<ClockCircleOutlined />}>{record.duration} ms</Tag> : 'unknown time ';
|
|
65
|
+
|
|
66
|
+
let statusCode: React.ReactNode;
|
|
67
|
+
if (record.statusCode === null) {
|
|
68
|
+
statusCode = <Tag>Unknown</Tag>;
|
|
69
|
+
} else if (record.statusCode >= 500) {
|
|
70
|
+
statusCode = <Tag color='red'>{record.statusCode}</Tag>;
|
|
71
|
+
} else if (record.statusCode >= 300) {
|
|
72
|
+
statusCode = <Tag color='orange'>{record.statusCode}</Tag>;
|
|
73
|
+
} else if (record.statusCode >= 200) {
|
|
74
|
+
statusCode = <Tag color='green'>{record.statusCode}</Tag>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// get query params form url
|
|
78
|
+
let queryParamsEl: React.ReactNode = <>N/A<br /></>;
|
|
79
|
+
if (record.url) {
|
|
80
|
+
let url: URL;
|
|
81
|
+
try {
|
|
82
|
+
url = new URL(window.location.origin + record.url);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
console.error(e);
|
|
85
|
+
url = new URL(window.location.origin);
|
|
86
|
+
}
|
|
87
|
+
const queryParams = Array.from(url.searchParams.entries());
|
|
88
|
+
if (queryParams.length) {
|
|
89
|
+
queryParamsEl = <Descriptions column={1} size='small' bordered={true} items={
|
|
90
|
+
queryParams.map(([key, value], i) => {
|
|
91
|
+
return {
|
|
92
|
+
key: i,
|
|
93
|
+
label: key,
|
|
94
|
+
children: key !== 's' ? <Text code>{value}</Text> : <pre style={{
|
|
95
|
+
fontSize: '0.8em',
|
|
96
|
+
}}>{formatJson(value)}</pre>
|
|
97
|
+
};
|
|
98
|
+
})
|
|
99
|
+
} />
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const query = <Paragraph>
|
|
103
|
+
<Title level={5}>Query Parameters</Title>
|
|
104
|
+
{queryParamsEl}
|
|
105
|
+
</Paragraph>;
|
|
106
|
+
|
|
107
|
+
let payloadEl: React.ReactNode = <>N/A<br /></>;
|
|
108
|
+
if (record.payload) {
|
|
109
|
+
payloadEl = <pre>{JSON.stringify(record.payload, null, 2).replace(/\n/g, '\n')}</pre>;
|
|
110
|
+
}
|
|
111
|
+
const payload = <Paragraph>
|
|
112
|
+
<Title level={5}>Action Payload</Title>
|
|
113
|
+
{payloadEl}
|
|
114
|
+
</Paragraph>;
|
|
115
|
+
|
|
116
|
+
let userEl = record.user
|
|
117
|
+
? <>User <Tag>{record.user.name}</Tag>which had role <Tag
|
|
118
|
+
icon={getUserRoleIcon(record.userRole)}>{record.userRole}</Tag></>
|
|
119
|
+
: <>A <Tag icon={<QuestionCircleOutlined />}>Guest</Tag></>;
|
|
120
|
+
|
|
121
|
+
if (record.userRole === EventLog.userRole.SYSTEM) {
|
|
122
|
+
userEl = <Tag icon={<SettingOutlined />}>System</Tag>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const actionEl = record.action === 'Access'
|
|
126
|
+
? <>tried to access <Tag>{record.entity}</Tag>controller</>
|
|
127
|
+
: <>performed <Tag>{record.entity}.{record.action}</Tag>action</>;
|
|
128
|
+
|
|
129
|
+
let logLevelEl = <Tag color={'warning'} icon={<WarningOutlined />}>{record.logLevel}</Tag>;
|
|
130
|
+
if (record.logLevel === EventLog.logLevel.INFO) {
|
|
131
|
+
logLevelEl = <Tag color={'blue'} icon={<InfoCircleOutlined />}>{record.logLevel}</Tag>;
|
|
132
|
+
} else if (record.logLevel === EventLog.logLevel.ERROR) {
|
|
133
|
+
logLevelEl = <Tag color={'red'} icon={<ExclamationCircleOutlined />}>{record.logLevel}</Tag>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return <Card
|
|
137
|
+
title={<>
|
|
138
|
+
{getLogTypeTag(record.logType)}captured at <Tag icon={<ClockCircleOutlined />}>{record.createdAt}</Tag>with log
|
|
139
|
+
level {logLevelEl}
|
|
140
|
+
</>}
|
|
141
|
+
>
|
|
142
|
+
{record.logType === EventLog.logType.AUDIT && <Paragraph>
|
|
143
|
+
{userEl}{actionEl} as result of the HTTP request<br />
|
|
144
|
+
<pre>
|
|
145
|
+
{record.method} {record.url}<br />
|
|
146
|
+
</pre>
|
|
147
|
+
{query}
|
|
148
|
+
{payload}
|
|
149
|
+
<Title level={5}>Client</Title>
|
|
150
|
+
Request was done from IP address <Tag>{record.ipAddress}</Tag><br />
|
|
151
|
+
{renderUserAgentText(record.userAgent)}
|
|
152
|
+
</Paragraph> || null}
|
|
153
|
+
{record.logType !== EventLog.logType.AUDIT && <><Paragraph>
|
|
154
|
+
{userEl}{actionEl}
|
|
155
|
+
</Paragraph>{payload}</>}
|
|
156
|
+
<Paragraph>
|
|
157
|
+
<Title level={5}>Service</Title>
|
|
158
|
+
The action was handled by service <Tag>{record.service}</Tag>{record.serviceId ? <>with ID <Text code
|
|
159
|
+
copyable>{record.serviceId}</Text></> : ''}
|
|
160
|
+
</Paragraph>
|
|
161
|
+
{(record.duration || record.statusCode) ? <Paragraph>
|
|
162
|
+
<Title level={5}>Response</Title>
|
|
163
|
+
The request took {duration}and returned status code {statusCode}<br />
|
|
164
|
+
</Paragraph> : null}
|
|
165
|
+
</Card>;
|
|
166
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import apiClient from "@@api/apiClient";
|
|
2
|
+
import { EventLog, EventLogCreateDto, EventLogTimelineQueryDto, EventLogUpdateDto } from "@@api/generated";
|
|
3
|
+
import { useEventLogsColumns } from "./useEventLogsColumns";
|
|
4
|
+
import { Table, TColumnsSet } from "@boarteam/boar-pack-common-frontend";
|
|
5
|
+
import { eventLogsSearchableColumns } from "./eventLogsSearchableColumns";
|
|
6
|
+
import { EventLogExplanation } from "./EventLogExplanation";
|
|
7
|
+
import { createStyles } from "antd-style";
|
|
8
|
+
import { ConfigProvider } from "antd";
|
|
9
|
+
|
|
10
|
+
type TEventLogFilterParams = {}
|
|
11
|
+
|
|
12
|
+
const columnsSets: TColumnsSet<EventLog>[] = [
|
|
13
|
+
{
|
|
14
|
+
name: 'Audit',
|
|
15
|
+
columns: [
|
|
16
|
+
'createdAt',
|
|
17
|
+
'logType',
|
|
18
|
+
'entity',
|
|
19
|
+
'action',
|
|
20
|
+
'entityId',
|
|
21
|
+
'user',
|
|
22
|
+
'logLevel',
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'Operational',
|
|
27
|
+
columns: [
|
|
28
|
+
'createdAt',
|
|
29
|
+
'logType',
|
|
30
|
+
'entity',
|
|
31
|
+
'entityId',
|
|
32
|
+
'service',
|
|
33
|
+
'serviceId',
|
|
34
|
+
'action',
|
|
35
|
+
'userRole',
|
|
36
|
+
'logLevel',
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Application',
|
|
41
|
+
columns: [
|
|
42
|
+
'createdAt',
|
|
43
|
+
'logType',
|
|
44
|
+
'service',
|
|
45
|
+
'serviceId',
|
|
46
|
+
'logLevel',
|
|
47
|
+
'payload',
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Requests',
|
|
52
|
+
columns: [
|
|
53
|
+
'service',
|
|
54
|
+
'createdAt',
|
|
55
|
+
'user',
|
|
56
|
+
'method',
|
|
57
|
+
'url',
|
|
58
|
+
'duration',
|
|
59
|
+
'statusCode',
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'HTTP',
|
|
64
|
+
columns: [
|
|
65
|
+
'createdAt',
|
|
66
|
+
'method',
|
|
67
|
+
'url',
|
|
68
|
+
'ipAddress',
|
|
69
|
+
'userAgent',
|
|
70
|
+
],
|
|
71
|
+
}
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const useStyles = createStyles(({ token }) => {
|
|
75
|
+
return {
|
|
76
|
+
table: {
|
|
77
|
+
'.ant-pro-table-list-toolbar-left': {
|
|
78
|
+
flex: '0 0 auto',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const EventLogsTable = ({
|
|
85
|
+
startTime,
|
|
86
|
+
endTime,
|
|
87
|
+
onDateRangeChange,
|
|
88
|
+
}: EventLogTimelineQueryDto & {
|
|
89
|
+
onDateRangeChange: (start: string | undefined, end: string | undefined) => void;
|
|
90
|
+
}) => {
|
|
91
|
+
const columns = useEventLogsColumns({
|
|
92
|
+
startTime,
|
|
93
|
+
endTime,
|
|
94
|
+
onDateRangeChange,
|
|
95
|
+
});
|
|
96
|
+
const { styles } = useStyles();
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<ConfigProvider
|
|
100
|
+
theme={{
|
|
101
|
+
components: {
|
|
102
|
+
Collapse: {
|
|
103
|
+
contentPadding: '0',
|
|
104
|
+
headerPadding: '0',
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
<Table<EventLog, EventLogCreateDto, EventLogUpdateDto, TEventLogFilterParams>
|
|
110
|
+
className={styles.table}
|
|
111
|
+
getAll={params => apiClient.eventLogs.getManyBaseEventLogsControllerEventLog(params)}
|
|
112
|
+
columns={columns}
|
|
113
|
+
idColumnName='id'
|
|
114
|
+
pathParams={{}}
|
|
115
|
+
defaultSort={['createdAt', 'DESC']}
|
|
116
|
+
columnsSets={columnsSets}
|
|
117
|
+
params={{
|
|
118
|
+
join: [{
|
|
119
|
+
field: 'user',
|
|
120
|
+
select: ['id', 'name'],
|
|
121
|
+
}],
|
|
122
|
+
baseFilters: {
|
|
123
|
+
...startTime && endTime && { createdAt: [startTime, endTime] },
|
|
124
|
+
}
|
|
125
|
+
}}
|
|
126
|
+
expandable={{
|
|
127
|
+
expandedRowRender: record => {
|
|
128
|
+
return <EventLogExplanation record={record} />;
|
|
129
|
+
},
|
|
130
|
+
fixed: 'left',
|
|
131
|
+
}}
|
|
132
|
+
viewOnly={true}
|
|
133
|
+
scroll={{
|
|
134
|
+
x: 'max-content',
|
|
135
|
+
}}
|
|
136
|
+
searchableColumns={eventLogsSearchableColumns}
|
|
137
|
+
search={{
|
|
138
|
+
filterType: 'light',
|
|
139
|
+
}}
|
|
140
|
+
ghost={true}
|
|
141
|
+
></Table>
|
|
142
|
+
</ConfigProvider>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default EventLogsTable;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { Column } from '@ant-design/plots';
|
|
3
|
+
import { ColumnConfig } from "@ant-design/plots/es/components/column";
|
|
4
|
+
import { EventLogTimelineDto, EventLogTimelineQueryDto } from "@@api/generated";
|
|
5
|
+
import apiClient from "@@api/apiClient";
|
|
6
|
+
import { Button } from "antd";
|
|
7
|
+
import { PageLoading } from "@ant-design/pro-layout";
|
|
8
|
+
import { useModel } from "umi";
|
|
9
|
+
|
|
10
|
+
type TEventLogsTimelineProps = EventLogTimelineQueryDto & {
|
|
11
|
+
onDateRangeChange: (start: string | undefined, end: string | undefined) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const EventLogsTimeline: React.FC<TEventLogsTimelineProps> = ({
|
|
15
|
+
startTime,
|
|
16
|
+
endTime,
|
|
17
|
+
onDateRangeChange,
|
|
18
|
+
}) => {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
const { initialState } = useModel('@@initialState');
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
const { navTheme } = initialState?.settings || {};
|
|
23
|
+
|
|
24
|
+
const [data, setData] = useState<EventLogTimelineDto[] | null>(null);
|
|
25
|
+
const sliderPosition = useMemo(() => [0, 1], []);
|
|
26
|
+
const [showFilterButton, setShowFilterButton] = useState(false);
|
|
27
|
+
|
|
28
|
+
const chartRef = React.useRef(null);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
apiClient.eventLogs.getTimeline({
|
|
32
|
+
startTime,
|
|
33
|
+
endTime,
|
|
34
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
35
|
+
}).then(setData).then(() => {
|
|
36
|
+
sliderPosition[0] = 0;
|
|
37
|
+
sliderPosition[1] = 1;
|
|
38
|
+
setShowFilterButton(false);
|
|
39
|
+
});
|
|
40
|
+
}, [startTime, endTime]);
|
|
41
|
+
|
|
42
|
+
if (!data) {
|
|
43
|
+
return <PageLoading/>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const applySliderDates = () => {
|
|
47
|
+
const [start, end] = sliderPosition;
|
|
48
|
+
const startDate = data?.[Math.floor(start * (data.length - 1))]?.startTime;
|
|
49
|
+
const endDate = data?.[Math.floor(end * (data.length - 1))]?.endTime;
|
|
50
|
+
onDateRangeChange(startDate, endDate);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const domain = ['Info', 'Warning', 'Error'];
|
|
54
|
+
|
|
55
|
+
const config: ColumnConfig = {
|
|
56
|
+
data,
|
|
57
|
+
xField: 'startTime',
|
|
58
|
+
yField: 'records',
|
|
59
|
+
colorField: 'logLevel',
|
|
60
|
+
stack: true,
|
|
61
|
+
height: 300,
|
|
62
|
+
theme: navTheme === 'realDark' ? 'dark' : 'light',
|
|
63
|
+
legend: {},
|
|
64
|
+
// slider: {
|
|
65
|
+
// x: {
|
|
66
|
+
// values: sliderPosition,
|
|
67
|
+
// onChange(values: [number, number]) {
|
|
68
|
+
// sliderPosition[0] = values[0];
|
|
69
|
+
// sliderPosition[1] = values[1];
|
|
70
|
+
// if (!showFilterButton) {
|
|
71
|
+
// setShowFilterButton(true);
|
|
72
|
+
// }
|
|
73
|
+
// }
|
|
74
|
+
// },
|
|
75
|
+
// },
|
|
76
|
+
axis: {
|
|
77
|
+
x: {
|
|
78
|
+
labelFormatter: (v: string, i: number) => {
|
|
79
|
+
return data[i * domain.length]?.time
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
y: {
|
|
83
|
+
gridLineWidth: 1,
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
scale: {
|
|
87
|
+
color: {
|
|
88
|
+
domain,
|
|
89
|
+
range: ['#1890ff', 'orange', 'red'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
onReady: ({chart}) => {
|
|
93
|
+
chart.on('interval:click', (event: any) => {
|
|
94
|
+
const {data} = event?.data || {};
|
|
95
|
+
onDateRangeChange(data?.startTime, data?.endTime);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return <>
|
|
101
|
+
<Column
|
|
102
|
+
{...config}
|
|
103
|
+
ref={chartRef}
|
|
104
|
+
/>
|
|
105
|
+
{
|
|
106
|
+
showFilterButton && <Button onClick={applySliderDates}>Filter dates</Button> || null
|
|
107
|
+
}
|
|
108
|
+
</>
|
|
109
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Bowser from 'bowser';
|
|
3
|
+
import { getBrowserIcon } from './getBrowserIcon';
|
|
4
|
+
|
|
5
|
+
type UserAgentProps = {
|
|
6
|
+
userAgent: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const UserAgentDisplay: React.FC<UserAgentProps> = ({ userAgent }) => {
|
|
10
|
+
const browserIconClass = getBrowserIcon(userAgent);
|
|
11
|
+
const browser = Bowser.getParser(userAgent).getBrowser();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<span>
|
|
15
|
+
<i className={browserIconClass} style={{ fontSize: '14px', marginRight: '4px' }}/>
|
|
16
|
+
<span>{`${browser.name} ${browser.version}`}</span>
|
|
17
|
+
</span>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default UserAgentDisplay;
|