nsgm-cli 2.1.13 → 2.1.14
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/README.md +370 -155
- package/client/components/Button.tsx +3 -5
- package/client/components/__tests__/Button.test.tsx +10 -10
- package/client/layout/index.tsx +149 -137
- package/client/redux/reducers.ts +1 -1
- package/client/redux/store.ts +2 -1
- package/client/redux/template/manage/actions.ts +77 -88
- package/client/redux/template/manage/reducers.ts +25 -37
- package/client/redux/template/manage/types.ts +1 -1
- package/client/service/template/manage.ts +20 -21
- package/client/styled/common.ts +12 -13
- package/client/styled/layout/index.ts +19 -19
- package/client/styled/template/manage.ts +14 -13
- package/client/utils/common.ts +23 -21
- package/client/utils/cookie.ts +18 -19
- package/client/utils/fetch.ts +64 -100
- package/client/utils/menu.tsx +16 -3
- package/client/utils/sso.ts +74 -84
- package/eslint.config.js +38 -1
- package/generation/README.md +25 -6
- package/generation/__tests__/example.test.js +41 -0
- package/generation/client/utils/menu.tsx +9 -2
- package/generation/env.example +3 -0
- package/generation/eslint.config.js +112 -0
- package/generation/gitignore +6 -1
- package/generation/jest.config.js +40 -0
- package/generation/package.json +24 -3
- package/jest.config.js +23 -6
- package/lib/args.js +9 -1
- package/lib/cli/app.d.ts +28 -0
- package/lib/cli/app.js +99 -0
- package/lib/cli/commands/build.d.ts +2 -0
- package/lib/cli/commands/build.js +29 -0
- package/lib/cli/commands/create.d.ts +2 -0
- package/lib/cli/commands/create.js +113 -0
- package/lib/cli/commands/delete.d.ts +3 -0
- package/lib/cli/commands/delete.js +151 -0
- package/lib/cli/commands/export.d.ts +2 -0
- package/lib/cli/commands/export.js +42 -0
- package/lib/cli/commands/help.d.ts +2 -0
- package/lib/cli/commands/help.js +42 -0
- package/lib/cli/commands/init.d.ts +2 -0
- package/lib/cli/commands/init.js +115 -0
- package/lib/cli/commands/server.d.ts +3 -0
- package/lib/cli/commands/server.js +26 -0
- package/lib/cli/commands/upgrade.d.ts +2 -0
- package/lib/cli/commands/upgrade.js +38 -0
- package/lib/cli/commands/version.d.ts +2 -0
- package/lib/cli/commands/version.js +24 -0
- package/lib/cli/index.d.ts +16 -0
- package/lib/cli/index.js +33 -0
- package/lib/cli/parser.d.ts +22 -0
- package/lib/cli/parser.js +115 -0
- package/lib/cli/registry.d.ts +33 -0
- package/lib/cli/registry.js +81 -0
- package/lib/cli/types/project.d.ts +10 -0
- package/lib/cli/types/project.js +2 -0
- package/lib/cli/types.d.ts +31 -0
- package/lib/cli/types.js +20 -0
- package/lib/cli/utils/console.d.ts +62 -0
- package/lib/cli/utils/console.js +148 -0
- package/lib/cli/utils/index.d.ts +2 -0
- package/lib/cli/utils/index.js +7 -0
- package/lib/cli/utils/prompt.d.ts +83 -0
- package/lib/cli/utils/prompt.js +368 -0
- package/lib/constants.d.ts +58 -0
- package/lib/constants.js +162 -0
- package/lib/generate.d.ts +25 -3
- package/lib/generate.js +97 -621
- package/lib/generate_create.d.ts +9 -0
- package/lib/generate_create.js +326 -0
- package/lib/generate_delete.d.ts +8 -0
- package/lib/generate_delete.js +156 -0
- package/lib/generate_init.d.ts +50 -0
- package/lib/generate_init.js +492 -0
- package/lib/generators/base-generator.d.ts +47 -0
- package/lib/generators/base-generator.js +92 -0
- package/lib/generators/generator-factory.d.ts +20 -0
- package/lib/generators/generator-factory.js +25 -0
- package/lib/generators/page-generator.d.ts +41 -0
- package/lib/generators/page-generator.js +552 -0
- package/lib/generators/resolver-generator.d.ts +12 -0
- package/lib/generators/resolver-generator.js +303 -0
- package/lib/generators/schema-generator.d.ts +7 -0
- package/lib/generators/schema-generator.js +57 -0
- package/lib/generators/service-generator.d.ts +7 -0
- package/lib/generators/service-generator.js +119 -0
- package/lib/generators/sql-generator.d.ts +8 -0
- package/lib/generators/sql-generator.js +52 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +14 -173
- package/lib/server/csrf.js +9 -16
- package/lib/server/db.js +6 -7
- package/lib/server/graphql.js +5 -6
- package/lib/server/plugins/date.js +1 -1
- package/lib/server/utils/graphql-cache.js +3 -3
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/lib/utils/project-config.d.ts +5 -0
- package/lib/utils/project-config.js +145 -0
- package/lib/utils.js +1 -1
- package/next.config.js +12 -8
- package/package.json +10 -7
- package/pages/_app.tsx +23 -28
- package/pages/_document.tsx +39 -19
- package/pages/index.tsx +84 -39
- package/pages/login.tsx +21 -21
- package/pages/template/manage.tsx +114 -89
- package/public/fonts/font-awesome.min.css +4 -0
- package/public/fonts/fontawesome-webfont.woff +0 -0
- package/public/fonts/fontawesome-webfont.woff2 +0 -0
- package/public/slbhealthcheck.html +1 -1
- package/server/apis/template.js +0 -2
- package/generation/eslintrc.js +0 -16
|
@@ -2,38 +2,38 @@ import styled from 'styled-components'
|
|
|
2
2
|
|
|
3
3
|
export const Container = styled.div`
|
|
4
4
|
margin: 20px;
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
.ant-table {
|
|
7
7
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
8
8
|
border-radius: 8px;
|
|
9
9
|
overflow: hidden;
|
|
10
10
|
transition: all 0.3s ease;
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
&:hover {
|
|
13
13
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
.table-row-light {
|
|
18
18
|
background-color: #ffffff;
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
.table-row-dark {
|
|
22
22
|
background-color: #f9f9f9;
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
.ant-table-thead > tr > th {
|
|
26
26
|
background-color: #f0f5ff;
|
|
27
27
|
color: #1890ff;
|
|
28
28
|
font-weight: 500;
|
|
29
29
|
padding: 12px 16px;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
.ant-table-tbody > tr > td {
|
|
33
33
|
padding: 12px 16px;
|
|
34
34
|
transition: background-color 0.3s;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
.page-title {
|
|
38
38
|
font-size: 24px;
|
|
39
39
|
font-weight: 500;
|
|
@@ -42,7 +42,7 @@ export const Container = styled.div`
|
|
|
42
42
|
padding-bottom: 10px;
|
|
43
43
|
border-bottom: 1px solid #f0f0f0;
|
|
44
44
|
position: relative;
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
&:after {
|
|
47
47
|
content: '';
|
|
48
48
|
position: absolute;
|
|
@@ -68,7 +68,7 @@ export const SearchRow = styled.div`
|
|
|
68
68
|
border-radius: 8px;
|
|
69
69
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
70
70
|
transition: all 0.3s ease;
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
&:hover {
|
|
73
73
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
74
74
|
}
|
|
@@ -95,7 +95,7 @@ export const ModalContainer = styled.div`
|
|
|
95
95
|
font-weight: 500;
|
|
96
96
|
color: #333;
|
|
97
97
|
position: relative;
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
&:after {
|
|
100
100
|
content: '';
|
|
101
101
|
position: absolute;
|
|
@@ -113,8 +113,9 @@ export const ModalContainer = styled.div`
|
|
|
113
113
|
flex: 1;
|
|
114
114
|
border-radius: 6px;
|
|
115
115
|
transition: all 0.3s ease;
|
|
116
|
-
|
|
117
|
-
&:hover,
|
|
116
|
+
|
|
117
|
+
&:hover,
|
|
118
|
+
&:focus {
|
|
118
119
|
border-color: #40a9ff;
|
|
119
120
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
|
120
121
|
}
|
|
@@ -133,4 +134,4 @@ export const ModalContainer = styled.div`
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
|
-
`
|
|
137
|
+
`
|
package/client/utils/common.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import getConfig from 'next/config'
|
|
2
2
|
import _ from 'lodash'
|
|
3
3
|
|
|
4
|
-
export const getLocalEnv = () => {
|
|
4
|
+
export const getLocalEnv = () => {
|
|
5
5
|
const nextConfig = getConfig()
|
|
6
6
|
const { publicRuntimeConfig } = nextConfig
|
|
7
7
|
let { env = 'uat' } = publicRuntimeConfig
|
|
@@ -12,75 +12,77 @@ export const getLocalEnv = () => {
|
|
|
12
12
|
export const getLocalApiPrefix = () => {
|
|
13
13
|
const nextConfig = getConfig()
|
|
14
14
|
const { publicRuntimeConfig } = nextConfig
|
|
15
|
-
let { protocol, host, port
|
|
15
|
+
let { protocol, host, port } = publicRuntimeConfig
|
|
16
|
+
const { prefix, isExport } = publicRuntimeConfig
|
|
16
17
|
|
|
17
18
|
let localApiPrefix = ''
|
|
18
|
-
|
|
19
|
-
if(!isExport){
|
|
20
|
-
if (typeof window !== 'undefined') {
|
|
19
|
+
|
|
20
|
+
if (!isExport) {
|
|
21
|
+
if (typeof window !== 'undefined') {
|
|
21
22
|
// 客户端:使用当前页面的 location
|
|
22
23
|
const location = window.location
|
|
23
24
|
|
|
24
25
|
protocol = location.protocol
|
|
25
|
-
if (protocol.indexOf(':') != -1) {
|
|
26
|
+
if (protocol.indexOf(':') != -1) {
|
|
26
27
|
protocol = protocol.split(':')[0]
|
|
27
28
|
}
|
|
28
29
|
host = location.hostname
|
|
29
|
-
port = location.port || (protocol.indexOf('https') !== -1 ?
|
|
30
|
+
port = location.port || (protocol.indexOf('https') !== -1 ? '443' : '80')
|
|
30
31
|
}
|
|
31
32
|
// 服务器端:直接使用配置中的值,无需额外处理
|
|
32
|
-
}
|
|
33
|
+
}
|
|
33
34
|
|
|
34
|
-
localApiPrefix = protocol
|
|
35
|
+
localApiPrefix = `${protocol}://${host}:${port}${prefix}`
|
|
35
36
|
return localApiPrefix
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export const handleXSS = (content: string) => {
|
|
39
40
|
content = content || ''
|
|
40
|
-
return content.replace(/</g, '<').replace(/>/g, '>').replace(
|
|
41
|
+
return content.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''')
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export const checkModalObj = (modalObj: {}, ignoreKeys: any = []) => {
|
|
44
45
|
let result: any = null
|
|
45
46
|
_.each(modalObj, (value: any, key: string) => {
|
|
46
|
-
// console.log('checkModalObj', ignoreKeys, key, ignoreKeys.join('.').indexOf(key))
|
|
47
47
|
if (ignoreKeys.length === 0 || (ignoreKeys.length !== 0 && ignoreKeys.join('.').indexOf(key) === -1)) {
|
|
48
48
|
if (_.trim(value) === '') {
|
|
49
49
|
result = {
|
|
50
50
|
key,
|
|
51
|
-
reason: '不能为空'
|
|
51
|
+
reason: '不能为空',
|
|
52
52
|
}
|
|
53
53
|
return false
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
return true
|
|
56
57
|
})
|
|
57
58
|
return result
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
export const getUrlParamByKey = (key: string) => {
|
|
61
|
+
export const getUrlParamByKey = (key: string) => {
|
|
61
62
|
let result = ''
|
|
62
63
|
if (typeof window !== 'undefined') {
|
|
63
64
|
const locationHref = window.location.href
|
|
64
|
-
if (locationHref.indexOf('?') !== -1) {
|
|
65
|
+
if (locationHref.indexOf('?') !== -1) {
|
|
65
66
|
const locationHrefArr = locationHref.split('?')
|
|
66
|
-
if (locationHrefArr.length > 1) {
|
|
67
|
+
if (locationHrefArr.length > 1) {
|
|
67
68
|
const paramsStr = locationHrefArr[1]
|
|
68
69
|
|
|
69
|
-
let params:any = []
|
|
70
|
+
let params: any = []
|
|
70
71
|
if (paramsStr.indexOf('&') !== -1) {
|
|
71
72
|
params = paramsStr.split('&')
|
|
72
|
-
} else {
|
|
73
|
+
} else {
|
|
73
74
|
params.push(paramsStr)
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
-
_.each(params, (item
|
|
77
|
-
if (item.indexOf('=') !== -1) {
|
|
76
|
+
|
|
77
|
+
_.each(params, (item) => {
|
|
78
|
+
if (item.indexOf('=') !== -1) {
|
|
78
79
|
const itemArr = item.split('=')
|
|
79
|
-
if (itemArr[0] === key) {
|
|
80
|
+
if (itemArr[0] === key) {
|
|
80
81
|
result = itemArr[1]
|
|
81
82
|
return false
|
|
82
83
|
}
|
|
83
84
|
}
|
|
85
|
+
return true
|
|
84
86
|
})
|
|
85
87
|
}
|
|
86
88
|
}
|
package/client/utils/cookie.ts
CHANGED
|
@@ -1,52 +1,51 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
2
|
|
|
3
|
-
export const setCookie = (name: string, value:string, cookieExpire:any) => {
|
|
3
|
+
export const setCookie = (name: string, value: string, cookieExpire: any) => {
|
|
4
4
|
const currentDate = new Date()
|
|
5
5
|
const currentTime = currentDate.getTime()
|
|
6
|
-
|
|
6
|
+
const expiresTime = new Date()
|
|
7
7
|
expiresTime.setTime(currentTime + 60 * 1000 * 60 * 24 * 30)
|
|
8
8
|
|
|
9
9
|
if (typeof document !== 'undefined') {
|
|
10
|
-
let cookie = name
|
|
10
|
+
let cookie = `${name}=${window.btoa(encodeURIComponent(value))}`
|
|
11
11
|
// let cookie = name + '=' + encodeURIComponent(value)
|
|
12
12
|
if (cookieExpire) {
|
|
13
|
-
cookie +=
|
|
14
|
-
} else {
|
|
15
|
-
cookie +=
|
|
13
|
+
cookie += `;expires=${cookieExpire}`
|
|
14
|
+
} else {
|
|
15
|
+
cookie += `;expires=${expiresTime.toUTCString()}`
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
cookie +=
|
|
18
|
+
cookie += ';path=/'
|
|
19
19
|
document.cookie = cookie
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export const getCookie = (name: string) => {
|
|
23
|
+
export const getCookie = (name: string) => {
|
|
24
24
|
let result = ''
|
|
25
|
-
if (typeof document !== 'undefined') {
|
|
25
|
+
if (typeof document !== 'undefined') {
|
|
26
26
|
const cookie = document.cookie
|
|
27
|
-
// console.log('cookie', cookie)
|
|
28
27
|
|
|
29
|
-
const cookieArr = cookie.split(
|
|
30
|
-
|
|
31
|
-
_.each(cookieArr, (item
|
|
32
|
-
const itemArr = item.split(
|
|
28
|
+
const cookieArr = cookie.split('; ')
|
|
29
|
+
|
|
30
|
+
_.each(cookieArr, (item) => {
|
|
31
|
+
const itemArr = item.split('=')
|
|
33
32
|
if (name === itemArr[0]) {
|
|
34
33
|
result = decodeURIComponent(window.atob(itemArr[1]))
|
|
35
|
-
// result = decodeURIComponent(itemArr[1])
|
|
36
34
|
return false
|
|
37
35
|
}
|
|
36
|
+
return true
|
|
38
37
|
})
|
|
39
38
|
}
|
|
40
39
|
return result
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
export const delCookie = (name: string) => {
|
|
42
|
+
export const delCookie = (name: string) => {
|
|
44
43
|
const currentDate = new Date()
|
|
45
44
|
const currentTime = currentDate.getTime()
|
|
46
|
-
|
|
45
|
+
const expiresTime = new Date()
|
|
47
46
|
expiresTime.setTime(currentTime - 1)
|
|
48
47
|
|
|
49
48
|
if (typeof document !== 'undefined') {
|
|
50
|
-
document.cookie = name
|
|
49
|
+
document.cookie = `${name}=;expires=${expiresTime.toUTCString()};path=/`
|
|
51
50
|
}
|
|
52
|
-
}
|
|
51
|
+
}
|
package/client/utils/fetch.ts
CHANGED
|
@@ -11,38 +11,38 @@ axios.defaults.withCredentials = true
|
|
|
11
11
|
export const GRAPHQL_CONFIG = {
|
|
12
12
|
// GraphQL 端点
|
|
13
13
|
endpoint: '/graphql',
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
// 默认请求头
|
|
16
16
|
defaultHeaders: {
|
|
17
17
|
'Content-Type': 'application/json',
|
|
18
|
-
|
|
18
|
+
Accept: 'application/json',
|
|
19
19
|
},
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
// 缓存配置
|
|
22
22
|
cache: {
|
|
23
23
|
defaultTTL: 5 * 60 * 1000, // 5分钟
|
|
24
24
|
maxSize: 100,
|
|
25
|
-
enabled: true
|
|
25
|
+
enabled: true,
|
|
26
26
|
},
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
// CSRF 配置
|
|
29
29
|
csrf: {
|
|
30
30
|
enabled: true,
|
|
31
31
|
tokenHeader: 'X-CSRF-Token',
|
|
32
|
-
cookieName: 'csrfToken'
|
|
32
|
+
cookieName: 'csrfToken',
|
|
33
33
|
},
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
// 开发模式配置
|
|
36
36
|
development: {
|
|
37
|
-
enableDebugLogs: process.env.NODE_ENV === 'development'
|
|
38
|
-
}
|
|
37
|
+
enableDebugLogs: process.env.NODE_ENV === 'development',
|
|
38
|
+
},
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// GraphQL 操作类型
|
|
42
42
|
export enum GraphQLOperationType {
|
|
43
43
|
QUERY = 'query',
|
|
44
44
|
MUTATION = 'mutation',
|
|
45
|
-
SUBSCRIPTION = 'subscription'
|
|
45
|
+
SUBSCRIPTION = 'subscription',
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// GraphQL 工具函数
|
|
@@ -54,20 +54,20 @@ export const GraphQLUtils = {
|
|
|
54
54
|
if (trimmed.startsWith('subscription')) return GraphQLOperationType.SUBSCRIPTION
|
|
55
55
|
return GraphQLOperationType.QUERY
|
|
56
56
|
},
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
// 提取操作名称
|
|
59
59
|
getOperationName(query: string): string | null {
|
|
60
60
|
const match = query.match(/(?:query|mutation|subscription)\s+(\w+)/)
|
|
61
61
|
return match ? match[1] : null
|
|
62
62
|
},
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
// 生成缓存键
|
|
65
65
|
generateCacheKey(query: string, variables?: any): string {
|
|
66
66
|
const operationName = this.getOperationName(query) || 'anonymous'
|
|
67
67
|
const variablesHash = variables ? JSON.stringify(variables) : ''
|
|
68
68
|
return `${operationName}_${btoa(variablesHash)}`
|
|
69
69
|
},
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
// 验证 GraphQL 查询语法
|
|
72
72
|
isValidQuery(query: string): boolean {
|
|
73
73
|
try {
|
|
@@ -81,7 +81,7 @@ export const GraphQLUtils = {
|
|
|
81
81
|
} catch {
|
|
82
82
|
return false
|
|
83
83
|
}
|
|
84
|
-
}
|
|
84
|
+
},
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// ==================== CSRF 工具 ====================
|
|
@@ -92,14 +92,14 @@ export const GraphQLUtils = {
|
|
|
92
92
|
*/
|
|
93
93
|
export const getCSRFToken = async (): Promise<string> => {
|
|
94
94
|
try {
|
|
95
|
-
const response = await axios.get(getLocalApiPrefix()
|
|
96
|
-
withCredentials: true
|
|
95
|
+
const response = await axios.get(`${getLocalApiPrefix()}/csrf-token`, {
|
|
96
|
+
withCredentials: true,
|
|
97
97
|
})
|
|
98
|
-
|
|
99
|
-
if (!response.data
|
|
98
|
+
|
|
99
|
+
if (!response.data?.csrfToken) {
|
|
100
100
|
throw new Error('服务器返回的 CSRF token 为空')
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
return response.data.csrfToken
|
|
104
104
|
} catch (error) {
|
|
105
105
|
console.error('获取 CSRF token 错误:', error)
|
|
@@ -108,46 +108,27 @@ export const getCSRFToken = async (): Promise<string> => {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// ==================== GraphQL 客户端 ====================
|
|
111
|
-
|
|
112
|
-
// GraphQL 查询缓存
|
|
113
|
-
const queryCache = new Map<string, { data: any; timestamp: number }>()
|
|
114
|
-
const CACHE_DURATION = GRAPHQL_CONFIG.cache.defaultTTL
|
|
115
|
-
|
|
116
111
|
/**
|
|
117
112
|
* GraphQL 客户端主函数
|
|
118
113
|
* 自动处理 CSRF 保护、缓存、错误重试
|
|
119
114
|
*/
|
|
120
|
-
export const getLocalGraphql = async (query: string, variables: any = {}
|
|
115
|
+
export const getLocalGraphql = async (query: string, variables: any = {}) => {
|
|
121
116
|
// 验证查询语法
|
|
122
117
|
if (!GraphQLUtils.isValidQuery(query)) {
|
|
123
118
|
throw new Error('Invalid GraphQL query syntax')
|
|
124
119
|
}
|
|
125
|
-
|
|
126
|
-
// 生成缓存键
|
|
127
|
-
const cacheKey = GraphQLUtils.generateCacheKey(query, variables)
|
|
128
|
-
|
|
129
|
-
// 检查缓存
|
|
130
|
-
if (useCache && queryCache.has(cacheKey)) {
|
|
131
|
-
const cached = queryCache.get(cacheKey)!
|
|
132
|
-
if (Date.now() - cached.timestamp < CACHE_DURATION) {
|
|
133
|
-
if (GRAPHQL_CONFIG.development.enableDebugLogs) {
|
|
134
|
-
console.log('GraphQL cache hit:', cacheKey)
|
|
135
|
-
}
|
|
136
|
-
return cached.data
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
120
|
+
|
|
140
121
|
try {
|
|
141
122
|
// 检测操作类型
|
|
142
123
|
const operationType = GraphQLUtils.getOperationType(query)
|
|
143
124
|
const isMutation = operationType === GraphQLOperationType.MUTATION
|
|
144
|
-
|
|
125
|
+
|
|
145
126
|
const headers: Record<string, string> = {
|
|
146
|
-
...GRAPHQL_CONFIG.defaultHeaders
|
|
127
|
+
...GRAPHQL_CONFIG.defaultHeaders,
|
|
147
128
|
}
|
|
148
|
-
|
|
129
|
+
|
|
149
130
|
let response
|
|
150
|
-
|
|
131
|
+
|
|
151
132
|
if (isMutation) {
|
|
152
133
|
// Mutation 使用 POST 方法并需要 CSRF token
|
|
153
134
|
if (GRAPHQL_CONFIG.csrf.enabled) {
|
|
@@ -158,16 +139,16 @@ export const getLocalGraphql = async (query: string, variables: any = {}, useCac
|
|
|
158
139
|
console.warn('获取 CSRF token 失败,继续执行 GraphQL 请求:', csrfError)
|
|
159
140
|
}
|
|
160
141
|
}
|
|
161
|
-
|
|
142
|
+
|
|
162
143
|
response = await axios.post(
|
|
163
|
-
getLocalApiPrefix()
|
|
144
|
+
`${getLocalApiPrefix()}/graphql`,
|
|
164
145
|
{
|
|
165
146
|
query,
|
|
166
|
-
variables
|
|
147
|
+
variables,
|
|
167
148
|
},
|
|
168
149
|
{
|
|
169
150
|
headers,
|
|
170
|
-
withCredentials: true
|
|
151
|
+
withCredentials: true,
|
|
171
152
|
}
|
|
172
153
|
)
|
|
173
154
|
} else {
|
|
@@ -177,27 +158,16 @@ export const getLocalGraphql = async (query: string, variables: any = {}, useCac
|
|
|
177
158
|
if (variables && Object.keys(variables).length > 0) {
|
|
178
159
|
params.append('variables', JSON.stringify(variables))
|
|
179
160
|
}
|
|
180
|
-
|
|
181
|
-
response = await axios.get(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
withCredentials: true
|
|
188
|
-
}
|
|
189
|
-
)
|
|
161
|
+
|
|
162
|
+
response = await axios.get(`${getLocalApiPrefix()}/graphql?${params.toString()}`, {
|
|
163
|
+
headers: {
|
|
164
|
+
Accept: 'application/json',
|
|
165
|
+
},
|
|
166
|
+
withCredentials: true,
|
|
167
|
+
})
|
|
190
168
|
}
|
|
191
169
|
|
|
192
|
-
if (response
|
|
193
|
-
// 缓存查询结果
|
|
194
|
-
if (useCache && !isMutation) {
|
|
195
|
-
queryCache.set(cacheKey, {
|
|
196
|
-
data: response.data,
|
|
197
|
-
timestamp: Date.now()
|
|
198
|
-
})
|
|
199
|
-
}
|
|
200
|
-
|
|
170
|
+
if (response?.data) {
|
|
201
171
|
return response.data
|
|
202
172
|
} else {
|
|
203
173
|
throw new Error('GraphQL response is empty')
|
|
@@ -206,7 +176,7 @@ export const getLocalGraphql = async (query: string, variables: any = {}, useCac
|
|
|
206
176
|
// 只为 mutation 检查 CSRF 错误 (403),因为 query 使用 GET 不需要 CSRF token
|
|
207
177
|
if (axios.isAxiosError(error) && error.response?.status === 403) {
|
|
208
178
|
const operationType = GraphQLUtils.getOperationType(query)
|
|
209
|
-
|
|
179
|
+
|
|
210
180
|
if (operationType === GraphQLOperationType.MUTATION) {
|
|
211
181
|
console.warn('🔄 CSRF token 可能已过期,尝试重试 mutation...')
|
|
212
182
|
try {
|
|
@@ -214,15 +184,15 @@ export const getLocalGraphql = async (query: string, variables: any = {}, useCac
|
|
|
214
184
|
const newCsrfToken = await getCSRFToken()
|
|
215
185
|
const retryHeaders = {
|
|
216
186
|
...GRAPHQL_CONFIG.defaultHeaders,
|
|
217
|
-
[GRAPHQL_CONFIG.csrf.tokenHeader]: newCsrfToken
|
|
187
|
+
[GRAPHQL_CONFIG.csrf.tokenHeader]: newCsrfToken,
|
|
218
188
|
}
|
|
219
|
-
|
|
189
|
+
|
|
220
190
|
const retryResponse = await axios.post(
|
|
221
|
-
getLocalApiPrefix()
|
|
191
|
+
`${getLocalApiPrefix()}/graphql`,
|
|
222
192
|
{ query, variables },
|
|
223
193
|
{ headers: retryHeaders, withCredentials: true }
|
|
224
194
|
)
|
|
225
|
-
|
|
195
|
+
|
|
226
196
|
return retryResponse.data
|
|
227
197
|
} catch (retryError) {
|
|
228
198
|
console.error('❌ CSRF mutation 重试失败:', retryError)
|
|
@@ -230,7 +200,7 @@ export const getLocalGraphql = async (query: string, variables: any = {}, useCac
|
|
|
230
200
|
}
|
|
231
201
|
}
|
|
232
202
|
}
|
|
233
|
-
|
|
203
|
+
|
|
234
204
|
console.error('GraphQL request failed:', error)
|
|
235
205
|
throw error
|
|
236
206
|
}
|
|
@@ -252,22 +222,14 @@ export const createCSRFUploadProps = (
|
|
|
252
222
|
multiple?: boolean
|
|
253
223
|
} = {}
|
|
254
224
|
) => {
|
|
255
|
-
const {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
onError,
|
|
259
|
-
beforeUpload: customBeforeUpload,
|
|
260
|
-
accept,
|
|
261
|
-
multiple = false
|
|
262
|
-
} = options
|
|
263
|
-
|
|
264
|
-
return {
|
|
225
|
+
const { name = 'file', onSuccess, onError, beforeUpload: customBeforeUpload, accept, multiple = false } = options
|
|
226
|
+
|
|
227
|
+
const uploadProps: any = {
|
|
265
228
|
name,
|
|
266
229
|
action,
|
|
267
|
-
accept,
|
|
268
230
|
multiple,
|
|
269
231
|
customRequest: async (options: any) => {
|
|
270
|
-
const {
|
|
232
|
+
const { onError: onUploadError, onSuccess: onUploadSuccess, file } = options
|
|
271
233
|
|
|
272
234
|
try {
|
|
273
235
|
// 获取 CSRF token
|
|
@@ -284,9 +246,9 @@ export const createCSRFUploadProps = (
|
|
|
284
246
|
const uploadUrl = action.startsWith('http') ? action : getLocalApiPrefix() + action
|
|
285
247
|
const response = await axios.post(uploadUrl, formData, {
|
|
286
248
|
headers: {
|
|
287
|
-
[GRAPHQL_CONFIG.csrf.tokenHeader]: csrfToken
|
|
249
|
+
[GRAPHQL_CONFIG.csrf.tokenHeader]: csrfToken,
|
|
288
250
|
},
|
|
289
|
-
withCredentials: true
|
|
251
|
+
withCredentials: true,
|
|
290
252
|
})
|
|
291
253
|
|
|
292
254
|
if (response.status >= 200 && response.status < 300) {
|
|
@@ -330,8 +292,15 @@ export const createCSRFUploadProps = (
|
|
|
330
292
|
onError(fileName)
|
|
331
293
|
}
|
|
332
294
|
}
|
|
333
|
-
}
|
|
295
|
+
},
|
|
334
296
|
}
|
|
297
|
+
|
|
298
|
+
// 只有当 accept 有值时才添加该属性
|
|
299
|
+
if (accept) {
|
|
300
|
+
uploadProps.accept = accept
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return uploadProps
|
|
335
304
|
}
|
|
336
305
|
|
|
337
306
|
/**
|
|
@@ -343,41 +312,36 @@ export const validateCSRFForUpload = async (): Promise<{ valid: boolean; token?:
|
|
|
343
312
|
if (!csrfToken) {
|
|
344
313
|
return {
|
|
345
314
|
valid: false,
|
|
346
|
-
error: 'CSRF Token 获取失败,请刷新页面重试'
|
|
315
|
+
error: 'CSRF Token 获取失败,请刷新页面重试',
|
|
347
316
|
}
|
|
348
317
|
}
|
|
349
318
|
return {
|
|
350
319
|
valid: true,
|
|
351
|
-
token: csrfToken
|
|
320
|
+
token: csrfToken,
|
|
352
321
|
}
|
|
353
322
|
} catch (error) {
|
|
354
323
|
return {
|
|
355
324
|
valid: false,
|
|
356
|
-
error: error instanceof Error ? error.message : '获取 CSRF Token 时发生未知错误'
|
|
325
|
+
error: error instanceof Error ? error.message : '获取 CSRF Token 时发生未知错误',
|
|
357
326
|
}
|
|
358
327
|
}
|
|
359
328
|
}
|
|
360
329
|
|
|
361
330
|
// ==================== 工具函数 ====================
|
|
362
331
|
|
|
363
|
-
// 清除缓存
|
|
364
|
-
export const clearGraphqlCache = () => {
|
|
365
|
-
queryCache.clear()
|
|
366
|
-
}
|
|
367
|
-
|
|
368
332
|
// GraphQL 查询辅助函数
|
|
369
|
-
export const graphqlQuery = async (query: string, variables?: any
|
|
370
|
-
return getLocalGraphql(query, variables
|
|
333
|
+
export const graphqlQuery = async (query: string, variables?: any) => {
|
|
334
|
+
return getLocalGraphql(query, variables)
|
|
371
335
|
}
|
|
372
336
|
|
|
373
337
|
// GraphQL 变更辅助函数 (Mutation)
|
|
374
338
|
export const graphqlMutation = async (mutation: string, variables?: any) => {
|
|
375
|
-
return getLocalGraphql(mutation, variables
|
|
339
|
+
return getLocalGraphql(mutation, variables)
|
|
376
340
|
}
|
|
377
341
|
|
|
378
342
|
// 检查 GraphQL 响应是否有错误
|
|
379
343
|
export const hasGraphqlErrors = (response: any): boolean => {
|
|
380
|
-
return response
|
|
344
|
+
return response?.errors && response.errors.length > 0
|
|
381
345
|
}
|
|
382
346
|
|
|
383
347
|
// 获取 GraphQL 错误信息
|
package/client/utils/menu.tsx
CHANGED
|
@@ -5,13 +5,26 @@ let key = 1
|
|
|
5
5
|
|
|
6
6
|
export default [
|
|
7
7
|
{
|
|
8
|
-
key: key.toString(),
|
|
8
|
+
key: (++key).toString(),
|
|
9
9
|
text: '介绍',
|
|
10
10
|
url: '/',
|
|
11
11
|
icon: <BookOutlined rev={undefined} />,
|
|
12
|
-
subMenus: null
|
|
12
|
+
subMenus: null,
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
|
+
key: (++key).toString(),
|
|
16
|
+
text: '模板',
|
|
17
|
+
url: '/template/manage',
|
|
18
|
+
icon: <SolutionOutlined rev={undefined} />,
|
|
19
|
+
subMenus: [
|
|
20
|
+
{
|
|
21
|
+
key: `${key}_1`,
|
|
22
|
+
text: '模板1',
|
|
23
|
+
url: '/template/manage',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
/*{
|
|
15
28
|
key: (++key).toString(),
|
|
16
29
|
text: '模板',
|
|
17
30
|
url: '/template/manage',
|
|
@@ -23,5 +36,5 @@ export default [
|
|
|
23
36
|
url: '/template/manage'
|
|
24
37
|
}
|
|
25
38
|
]
|
|
26
|
-
}
|
|
39
|
+
}*/
|
|
27
40
|
]
|