nsgm-cli 2.1.9 → 2.1.11
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 +84 -57
- package/client/redux/store.ts +13 -0
- package/client/redux/template/manage/actions.ts +8 -28
- package/client/service/template/manage.ts +8 -8
- package/client/utils/common.ts +2 -2
- package/client/utils/fetch.ts +371 -36
- package/client/utils/sso.ts +54 -14
- package/generation/README.md +68 -28
- package/lib/index.js +34 -1
- package/lib/server/csrf.d.ts +17 -0
- package/lib/server/csrf.js +99 -0
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/next.config.js +6 -0
- package/package.json +10 -2
- package/pages/_app.tsx +35 -4
- package/pages/login.tsx +22 -5
- package/pages/template/manage.tsx +35 -27
- package/scripts/generate-password-hash.js +43 -0
- package/server/apis/sso.js +97 -22
package/README.md
CHANGED
|
@@ -16,26 +16,28 @@ A full-stack development framework with code template generation capabilities, h
|
|
|
16
16
|
- Rapid development workflow
|
|
17
17
|
- Integrated GraphQL API
|
|
18
18
|
- MySQL database support
|
|
19
|
+
- Secure login system with bcrypt encryption
|
|
19
20
|
|
|
20
21
|
## Command Line Tools
|
|
21
22
|
|
|
22
23
|
### Basic Commands
|
|
23
24
|
|
|
24
|
-
| Command
|
|
25
|
-
|
|
26
|
-
| `nsgm init`
|
|
27
|
-
| `nsgm upgrade`
|
|
28
|
-
| `nsgm create`
|
|
29
|
-
| `nsgm delete`
|
|
25
|
+
| Command | Description |
|
|
26
|
+
| --------------- | --------------------------------------- |
|
|
27
|
+
| `nsgm init` | Initialize project |
|
|
28
|
+
| `nsgm upgrade` | Upgrade project base files |
|
|
29
|
+
| `nsgm create` | Create template page |
|
|
30
|
+
| `nsgm delete` | Delete template page |
|
|
30
31
|
| `nsgm deletedb` | Delete template page and database table |
|
|
31
|
-
| `nsgm dev`
|
|
32
|
-
| `nsgm start`
|
|
33
|
-
| `nsgm build`
|
|
34
|
-
| `nsgm export`
|
|
32
|
+
| `nsgm dev` | Development mode |
|
|
33
|
+
| `nsgm start` | Production mode |
|
|
34
|
+
| `nsgm build` | Build project |
|
|
35
|
+
| `nsgm export` | Export static pages |
|
|
35
36
|
|
|
36
37
|
### Parameter Description
|
|
37
38
|
|
|
38
39
|
- **dictionary**: Used with `export`/`init` commands, default value is `webapp`
|
|
40
|
+
|
|
39
41
|
```
|
|
40
42
|
nsgm init dictionary=webapp
|
|
41
43
|
# or simplified as
|
|
@@ -43,6 +45,7 @@ A full-stack development framework with code template generation capabilities, h
|
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
- **controller**: Used with `create`/`delete` commands, required parameter
|
|
48
|
+
|
|
46
49
|
```
|
|
47
50
|
nsgm create math
|
|
48
51
|
```
|
|
@@ -52,6 +55,29 @@ A full-stack development framework with code template generation capabilities, h
|
|
|
52
55
|
nsgm create math test
|
|
53
56
|
```
|
|
54
57
|
|
|
58
|
+
## Security Configuration
|
|
59
|
+
|
|
60
|
+
For security setup and login configuration, please refer to [SECURITY.md](./SECURITY.md).
|
|
61
|
+
|
|
62
|
+
### Quick Setup
|
|
63
|
+
|
|
64
|
+
1. Generate password hash:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm run generate-password yourSecurePassword
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
2. Create `.env` file:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
LOGIN_USERNAME=admin
|
|
74
|
+
LOGIN_PASSWORD_HASH=your_generated_hash_here
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. Make sure `.env` is in your `.gitignore` file.
|
|
78
|
+
|
|
79
|
+
**⚠️ Important:** Never commit passwords or `.env` files to version control.
|
|
80
|
+
|
|
55
81
|
## Project Configuration
|
|
56
82
|
|
|
57
83
|
### next.config.js
|
|
@@ -60,14 +86,17 @@ A full-stack development framework with code template generation capabilities, h
|
|
|
60
86
|
const { nextConfig } = require('nsgm-cli')
|
|
61
87
|
const projectConfig = require('./project.config')
|
|
62
88
|
|
|
63
|
-
const { version, prefix, protocol, host } = projectConfig
|
|
89
|
+
const { version, prefix, protocol, host } = projectConfig
|
|
64
90
|
|
|
65
91
|
module.exports = (phase, defaultConfig) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
92
|
+
let configObj = nextConfig(phase, defaultConfig, {
|
|
93
|
+
version,
|
|
94
|
+
prefix,
|
|
95
|
+
protocol,
|
|
96
|
+
host
|
|
97
|
+
})
|
|
69
98
|
|
|
70
|
-
|
|
99
|
+
return configObj
|
|
71
100
|
}
|
|
72
101
|
```
|
|
73
102
|
|
|
@@ -79,13 +108,13 @@ const { mysqlOptions } = mysqlConfig
|
|
|
79
108
|
const { user, password, host, port, database } = mysqlOptions
|
|
80
109
|
|
|
81
110
|
module.exports = {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
111
|
+
mysqlOptions: {
|
|
112
|
+
user,
|
|
113
|
+
password,
|
|
114
|
+
host,
|
|
115
|
+
port,
|
|
116
|
+
database
|
|
117
|
+
}
|
|
89
118
|
}
|
|
90
119
|
```
|
|
91
120
|
|
|
@@ -99,11 +128,11 @@ const { prefix, protocol, host, port } = projectConfig
|
|
|
99
128
|
const { version } = pkg
|
|
100
129
|
|
|
101
130
|
module.exports = {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
version,
|
|
132
|
+
prefix,
|
|
133
|
+
protocol,
|
|
134
|
+
host,
|
|
135
|
+
port
|
|
107
136
|
}
|
|
108
137
|
```
|
|
109
138
|
|
|
@@ -116,11 +145,11 @@ The `server` folder in the project root contains the following:
|
|
|
116
145
|
- `apis/` - Stores REST API interfaces
|
|
117
146
|
- `modules/` - Stores GraphQL resolvers and schemas
|
|
118
147
|
- `plugins/` - Stores GraphQL plugins
|
|
119
|
-
- `*.js` - Route files, e.g., `test.js` handles requests to `/test/*` and `${prefix}/test/*`
|
|
148
|
+
- `*.js` - Route files, e.g., `csrf-test.js` handles requests to `/csrf-test/*` and `${prefix}/csrf-test/*`
|
|
120
149
|
|
|
121
150
|
### Example Code
|
|
122
151
|
|
|
123
|
-
#### Route File Example (server/test.js)
|
|
152
|
+
#### Route File Example (server/csrf-test.js)
|
|
124
153
|
|
|
125
154
|
```javascript
|
|
126
155
|
const express = require('express')
|
|
@@ -129,14 +158,14 @@ const moment = require('moment')
|
|
|
129
158
|
const router = express.Router()
|
|
130
159
|
|
|
131
160
|
router.use((req, res, next) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
161
|
+
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
|
|
162
|
+
console.log(moment().format('YYYY-MM-DD HH:mm:ss') + ' ' + fullUrl)
|
|
163
|
+
next()
|
|
135
164
|
})
|
|
136
165
|
|
|
137
166
|
router.get('/*', (req, res) => {
|
|
138
|
-
|
|
139
|
-
|
|
167
|
+
res.statusCode = 200
|
|
168
|
+
res.json({ name: 'TEST' })
|
|
140
169
|
})
|
|
141
170
|
|
|
142
171
|
module.exports = router
|
|
@@ -149,8 +178,8 @@ const express = require('express')
|
|
|
149
178
|
const router = express.Router()
|
|
150
179
|
|
|
151
180
|
router.get('/*', (req, res) => {
|
|
152
|
-
|
|
153
|
-
|
|
181
|
+
res.statusCode = 200
|
|
182
|
+
res.json({ name: 'Hello' })
|
|
154
183
|
})
|
|
155
184
|
|
|
156
185
|
module.exports = router
|
|
@@ -160,15 +189,15 @@ module.exports = router
|
|
|
160
189
|
|
|
161
190
|
```javascript
|
|
162
191
|
module.exports = {
|
|
163
|
-
|
|
192
|
+
query: `
|
|
164
193
|
link: String
|
|
165
194
|
`,
|
|
166
|
-
|
|
195
|
+
mutation: `
|
|
167
196
|
linkUpdate(link: Date): String
|
|
168
197
|
`,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
198
|
+
subscription: ``,
|
|
199
|
+
type: ``
|
|
200
|
+
}
|
|
172
201
|
```
|
|
173
202
|
|
|
174
203
|
#### GraphQL Resolver Example (server/modules/link/resolver.js)
|
|
@@ -177,14 +206,14 @@ module.exports = {
|
|
|
177
206
|
let localLink = ''
|
|
178
207
|
|
|
179
208
|
module.exports = {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
209
|
+
link: () => {
|
|
210
|
+
return localLink
|
|
211
|
+
},
|
|
212
|
+
linkUpdate: ({ link }) => {
|
|
213
|
+
console.log('link', link)
|
|
214
|
+
localLink = link
|
|
215
|
+
return localLink
|
|
216
|
+
}
|
|
188
217
|
}
|
|
189
218
|
```
|
|
190
219
|
|
|
@@ -196,14 +225,12 @@ const { Kind } = require('graphql/language')
|
|
|
196
225
|
const { GraphQLScalarType } = require('graphql')
|
|
197
226
|
|
|
198
227
|
const customScalarDate = new GraphQLScalarType({
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
? parseInt(ast.value, 10)
|
|
205
|
-
: null
|
|
228
|
+
name: 'Date',
|
|
229
|
+
description: 'Date custom scalar type',
|
|
230
|
+
parseValue: (value) => moment(value).valueOf(),
|
|
231
|
+
serialize: (value) => moment(value).format('YYYY-MM-DD HH:mm:ss:SSS'),
|
|
232
|
+
parseLiteral: (ast) => (ast.kind === Kind.INT ? parseInt(ast.value, 10) : null)
|
|
206
233
|
})
|
|
207
234
|
|
|
208
235
|
module.exports = { Date: customScalarDate }
|
|
209
|
-
```
|
|
236
|
+
```
|
package/client/redux/store.ts
CHANGED
|
@@ -15,6 +15,19 @@ if (reducersKeysLen > 0) {
|
|
|
15
15
|
|
|
16
16
|
export type RootState = ReturnType<typeof combineReducer>
|
|
17
17
|
|
|
18
|
+
// 创建一个临时 store 实例来获取正确的 dispatch 类型
|
|
19
|
+
const tempStore = configureStore({
|
|
20
|
+
reducer: combineReducer,
|
|
21
|
+
middleware: (getDefaultMiddleware) =>
|
|
22
|
+
getDefaultMiddleware({
|
|
23
|
+
serializableCheck: {
|
|
24
|
+
ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export type AppDispatch = typeof tempStore.dispatch
|
|
30
|
+
|
|
18
31
|
function initStore(initialState?: any): EnhancedStore {
|
|
19
32
|
return configureStore({
|
|
20
33
|
reducer: combineReducer,
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as types from './types'
|
|
2
2
|
import { getTemplateService, addTemplateService, updateTemplateService, deleteTemplateService, searchTemplateService, batchDeleteTemplateService } from '@/service/template/manage'
|
|
3
|
+
import { AppDispatch } from '@/redux/store'
|
|
3
4
|
|
|
4
5
|
export const getTemplate = (page=0, pageSize=10) => (
|
|
5
|
-
dispatch:
|
|
6
|
-
type: string
|
|
7
|
-
payload?: { template: any }
|
|
8
|
-
}) => void
|
|
6
|
+
dispatch: AppDispatch
|
|
9
7
|
) => {
|
|
10
8
|
dispatch({
|
|
11
9
|
type: types.GET_TEMPLATE
|
|
@@ -30,10 +28,7 @@ export const getTemplate = (page=0, pageSize=10) => (
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
export const searchTemplate = (page=0, pageSize=10, data: any) => (
|
|
33
|
-
dispatch:
|
|
34
|
-
type: string
|
|
35
|
-
payload?: { template: any }
|
|
36
|
-
}) => void
|
|
31
|
+
dispatch: AppDispatch
|
|
37
32
|
) => {
|
|
38
33
|
dispatch({
|
|
39
34
|
type: types.SEARCH_TEMPLATE
|
|
@@ -58,10 +53,7 @@ export const searchTemplate = (page=0, pageSize=10, data: any) => (
|
|
|
58
53
|
}
|
|
59
54
|
|
|
60
55
|
export const updateSSRTemplate = (template: any) => (
|
|
61
|
-
dispatch:
|
|
62
|
-
type: string
|
|
63
|
-
payload?: { template: any }
|
|
64
|
-
}) => void
|
|
56
|
+
dispatch: AppDispatch
|
|
65
57
|
) => {
|
|
66
58
|
dispatch({
|
|
67
59
|
type: types.UPDATE_SSR_TEMPLATE,
|
|
@@ -72,10 +64,7 @@ export const updateSSRTemplate = (template: any) => (
|
|
|
72
64
|
}
|
|
73
65
|
|
|
74
66
|
export const addTemplate = (obj:any) => (
|
|
75
|
-
dispatch:
|
|
76
|
-
type: string
|
|
77
|
-
payload?: { template: any }
|
|
78
|
-
}) => void
|
|
67
|
+
dispatch: AppDispatch
|
|
79
68
|
) => {
|
|
80
69
|
dispatch({
|
|
81
70
|
type: types.ADD_TEMPLATE
|
|
@@ -104,10 +93,7 @@ export const addTemplate = (obj:any) => (
|
|
|
104
93
|
}
|
|
105
94
|
|
|
106
95
|
export const modTemplate = (id: number, obj: any) => (
|
|
107
|
-
dispatch:
|
|
108
|
-
type: string
|
|
109
|
-
payload?: { template: any }
|
|
110
|
-
}) => void
|
|
96
|
+
dispatch: AppDispatch
|
|
111
97
|
) => {
|
|
112
98
|
dispatch({
|
|
113
99
|
type: types.MOD_TEMPLATE
|
|
@@ -135,10 +121,7 @@ export const modTemplate = (id: number, obj: any) => (
|
|
|
135
121
|
}
|
|
136
122
|
|
|
137
123
|
export const delTemplate = (id: number) => (
|
|
138
|
-
dispatch:
|
|
139
|
-
type: string
|
|
140
|
-
payload?: { id: number }
|
|
141
|
-
}) => void
|
|
124
|
+
dispatch: AppDispatch
|
|
142
125
|
) => {
|
|
143
126
|
dispatch({
|
|
144
127
|
type: types.DEL_TEMPLATE
|
|
@@ -163,10 +146,7 @@ export const delTemplate = (id: number) => (
|
|
|
163
146
|
}
|
|
164
147
|
|
|
165
148
|
export const batchDelTemplate = (ids:any) => (
|
|
166
|
-
dispatch:
|
|
167
|
-
type: string
|
|
168
|
-
payload?: { ids: any }
|
|
169
|
-
}) => void
|
|
149
|
+
dispatch: AppDispatch
|
|
170
150
|
) => {
|
|
171
151
|
dispatch({
|
|
172
152
|
type: types.BATCH_DEL_TEMPLATE
|
|
@@ -12,7 +12,7 @@ export const getTemplateService = (page = 0, pageSize = 10) => {
|
|
|
12
12
|
return getLocalGraphql(getTemplateQuery, {
|
|
13
13
|
page,
|
|
14
14
|
pageSize
|
|
15
|
-
})
|
|
15
|
+
}, true) // 启用缓存,因为这是查询操作
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export const searchTemplateByIdService = (id: number) => {
|
|
@@ -24,7 +24,7 @@ export const searchTemplateByIdService = (id: number) => {
|
|
|
24
24
|
|
|
25
25
|
return getLocalGraphql(searchTemplateByIdQuery, {
|
|
26
26
|
id
|
|
27
|
-
})
|
|
27
|
+
}, true) // 启用缓存,因为这是查询操作
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export const searchTemplateService = (page = 0, pageSize = 10, data: any) => {
|
|
@@ -44,7 +44,7 @@ export const searchTemplateService = (page = 0, pageSize = 10, data: any) => {
|
|
|
44
44
|
data: {
|
|
45
45
|
name
|
|
46
46
|
}
|
|
47
|
-
})
|
|
47
|
+
}, true) // 启用缓存,因为这是查询操作
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export const addTemplateService = (data: any) => {
|
|
@@ -56,7 +56,7 @@ export const addTemplateService = (data: any) => {
|
|
|
56
56
|
data: {
|
|
57
57
|
name
|
|
58
58
|
}
|
|
59
|
-
})
|
|
59
|
+
}, false) // 不使用缓存,因为这是变更操作,会自动添加 CSRF token
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export const updateTemplateService = (id: number, data: any) => {
|
|
@@ -69,7 +69,7 @@ export const updateTemplateService = (id: number, data: any) => {
|
|
|
69
69
|
data: {
|
|
70
70
|
name
|
|
71
71
|
}
|
|
72
|
-
})
|
|
72
|
+
}, false) // 不使用缓存,因为这是变更操作,会自动添加 CSRF token
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
export const deleteTemplateService = (id: number) => {
|
|
@@ -77,7 +77,7 @@ export const deleteTemplateService = (id: number) => {
|
|
|
77
77
|
|
|
78
78
|
return getLocalGraphql(deleteTemplateQuery, {
|
|
79
79
|
id
|
|
80
|
-
})
|
|
80
|
+
}, false) // 不使用缓存,因为这是变更操作,会自动添加 CSRF token
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export const batchAddTemplateService = (datas: any) => {
|
|
@@ -85,7 +85,7 @@ export const batchAddTemplateService = (datas: any) => {
|
|
|
85
85
|
|
|
86
86
|
return getLocalGraphql(batchAddTemplateQuery, {
|
|
87
87
|
datas
|
|
88
|
-
})
|
|
88
|
+
}, false) // 不使用缓存,因为这是变更操作,会自动添加 CSRF token
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
export const batchDeleteTemplateService = (ids: any) => {
|
|
@@ -93,5 +93,5 @@ export const batchDeleteTemplateService = (ids: any) => {
|
|
|
93
93
|
|
|
94
94
|
return getLocalGraphql(batchDeleteTemplateQuery, {
|
|
95
95
|
ids
|
|
96
|
-
})
|
|
96
|
+
}, false) // 不使用缓存,因为这是变更操作,会自动添加 CSRF token
|
|
97
97
|
}
|
package/client/utils/common.ts
CHANGED
|
@@ -18,8 +18,8 @@ export const getLocalApiPrefix = () => {
|
|
|
18
18
|
|
|
19
19
|
if(!isExport){
|
|
20
20
|
if (typeof window !== 'undefined') {
|
|
21
|
+
// 客户端:使用当前页面的 location
|
|
21
22
|
const location = window.location
|
|
22
|
-
// console.log('location', location)
|
|
23
23
|
|
|
24
24
|
protocol = location.protocol
|
|
25
25
|
if (protocol.indexOf(':') != -1) {
|
|
@@ -28,10 +28,10 @@ export const getLocalApiPrefix = () => {
|
|
|
28
28
|
host = location.hostname
|
|
29
29
|
port = location.port || (protocol.indexOf('https') !== -1 ? "443" : "80")
|
|
30
30
|
}
|
|
31
|
+
// 服务器端:直接使用配置中的值,无需额外处理
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
localApiPrefix = protocol + '://' + host + ':' + port + prefix
|
|
34
|
-
// console.log('localApiPrefix', localApiPrefix)
|
|
35
35
|
return localApiPrefix
|
|
36
36
|
}
|
|
37
37
|
|