nsgm-cli 2.1.10 → 2.1.12
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 +95 -58
- 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/generation/README.md +81 -28
- package/generation/package.json +1 -0
- package/lib/generate.js +5 -0
- package/lib/index.js +54 -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/login.tsx +17 -10
- package/pages/template/manage.tsx +35 -27
- package/scripts/generate-password-hash.js +43 -0
- package/server/apis/sso.js +94 -21
package/README.md
CHANGED
|
@@ -16,26 +16,29 @@ 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`
|
|
30
|
-
| `nsgm deletedb`
|
|
31
|
-
| `nsgm
|
|
32
|
-
| `nsgm
|
|
33
|
-
| `nsgm
|
|
34
|
-
| `nsgm
|
|
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 |
|
|
31
|
+
| `nsgm deletedb` | Delete template page and database table |
|
|
32
|
+
| `nsgm password` | Generate password hash |
|
|
33
|
+
| `nsgm dev` | Development mode |
|
|
34
|
+
| `nsgm start` | Production mode |
|
|
35
|
+
| `nsgm build` | Build project |
|
|
36
|
+
| `nsgm export` | Export static pages |
|
|
35
37
|
|
|
36
38
|
### Parameter Description
|
|
37
39
|
|
|
38
40
|
- **dictionary**: Used with `export`/`init` commands, default value is `webapp`
|
|
41
|
+
|
|
39
42
|
```
|
|
40
43
|
nsgm init dictionary=webapp
|
|
41
44
|
# or simplified as
|
|
@@ -43,6 +46,7 @@ A full-stack development framework with code template generation capabilities, h
|
|
|
43
46
|
```
|
|
44
47
|
|
|
45
48
|
- **controller**: Used with `create`/`delete` commands, required parameter
|
|
49
|
+
|
|
46
50
|
```
|
|
47
51
|
nsgm create math
|
|
48
52
|
```
|
|
@@ -52,6 +56,38 @@ A full-stack development framework with code template generation capabilities, h
|
|
|
52
56
|
nsgm create math test
|
|
53
57
|
```
|
|
54
58
|
|
|
59
|
+
- **password**: Used with `password` command, optional parameter
|
|
60
|
+
```
|
|
61
|
+
nsgm password yourSecurePassword
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Security Configuration
|
|
65
|
+
|
|
66
|
+
For security setup and login configuration, please refer to [SECURITY.md](./SECURITY.md).
|
|
67
|
+
|
|
68
|
+
### Quick Setup
|
|
69
|
+
|
|
70
|
+
1. Generate password hash:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Using npm script
|
|
74
|
+
npm run generate-password yourSecurePassword
|
|
75
|
+
|
|
76
|
+
# Or using nsgm directly
|
|
77
|
+
nsgm password yourSecurePassword
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
2. Create `.env` file:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
LOGIN_USERNAME=admin
|
|
84
|
+
LOGIN_PASSWORD_HASH=your_generated_hash_here
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
3. Make sure `.env` is in your `.gitignore` file.
|
|
88
|
+
|
|
89
|
+
**⚠️ Important:** Never commit passwords or `.env` files to version control.
|
|
90
|
+
|
|
55
91
|
## Project Configuration
|
|
56
92
|
|
|
57
93
|
### next.config.js
|
|
@@ -60,14 +96,17 @@ A full-stack development framework with code template generation capabilities, h
|
|
|
60
96
|
const { nextConfig } = require('nsgm-cli')
|
|
61
97
|
const projectConfig = require('./project.config')
|
|
62
98
|
|
|
63
|
-
const { version, prefix, protocol, host } = projectConfig
|
|
99
|
+
const { version, prefix, protocol, host } = projectConfig
|
|
64
100
|
|
|
65
101
|
module.exports = (phase, defaultConfig) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
102
|
+
let configObj = nextConfig(phase, defaultConfig, {
|
|
103
|
+
version,
|
|
104
|
+
prefix,
|
|
105
|
+
protocol,
|
|
106
|
+
host
|
|
107
|
+
})
|
|
69
108
|
|
|
70
|
-
|
|
109
|
+
return configObj
|
|
71
110
|
}
|
|
72
111
|
```
|
|
73
112
|
|
|
@@ -79,13 +118,13 @@ const { mysqlOptions } = mysqlConfig
|
|
|
79
118
|
const { user, password, host, port, database } = mysqlOptions
|
|
80
119
|
|
|
81
120
|
module.exports = {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
121
|
+
mysqlOptions: {
|
|
122
|
+
user,
|
|
123
|
+
password,
|
|
124
|
+
host,
|
|
125
|
+
port,
|
|
126
|
+
database
|
|
127
|
+
}
|
|
89
128
|
}
|
|
90
129
|
```
|
|
91
130
|
|
|
@@ -99,11 +138,11 @@ const { prefix, protocol, host, port } = projectConfig
|
|
|
99
138
|
const { version } = pkg
|
|
100
139
|
|
|
101
140
|
module.exports = {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
141
|
+
version,
|
|
142
|
+
prefix,
|
|
143
|
+
protocol,
|
|
144
|
+
host,
|
|
145
|
+
port
|
|
107
146
|
}
|
|
108
147
|
```
|
|
109
148
|
|
|
@@ -116,11 +155,11 @@ The `server` folder in the project root contains the following:
|
|
|
116
155
|
- `apis/` - Stores REST API interfaces
|
|
117
156
|
- `modules/` - Stores GraphQL resolvers and schemas
|
|
118
157
|
- `plugins/` - Stores GraphQL plugins
|
|
119
|
-
- `*.js` - Route files
|
|
158
|
+
- `*.js` - Route files
|
|
120
159
|
|
|
121
160
|
### Example Code
|
|
122
161
|
|
|
123
|
-
#### Route File Example (server/test.js)
|
|
162
|
+
#### Route File Example (server/csrf-test.js)
|
|
124
163
|
|
|
125
164
|
```javascript
|
|
126
165
|
const express = require('express')
|
|
@@ -129,14 +168,14 @@ const moment = require('moment')
|
|
|
129
168
|
const router = express.Router()
|
|
130
169
|
|
|
131
170
|
router.use((req, res, next) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
171
|
+
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
|
|
172
|
+
console.log(moment().format('YYYY-MM-DD HH:mm:ss') + ' ' + fullUrl)
|
|
173
|
+
next()
|
|
135
174
|
})
|
|
136
175
|
|
|
137
176
|
router.get('/*', (req, res) => {
|
|
138
|
-
|
|
139
|
-
|
|
177
|
+
res.statusCode = 200
|
|
178
|
+
res.json({ name: 'TEST' })
|
|
140
179
|
})
|
|
141
180
|
|
|
142
181
|
module.exports = router
|
|
@@ -149,8 +188,8 @@ const express = require('express')
|
|
|
149
188
|
const router = express.Router()
|
|
150
189
|
|
|
151
190
|
router.get('/*', (req, res) => {
|
|
152
|
-
|
|
153
|
-
|
|
191
|
+
res.statusCode = 200
|
|
192
|
+
res.json({ name: 'Hello' })
|
|
154
193
|
})
|
|
155
194
|
|
|
156
195
|
module.exports = router
|
|
@@ -160,15 +199,15 @@ module.exports = router
|
|
|
160
199
|
|
|
161
200
|
```javascript
|
|
162
201
|
module.exports = {
|
|
163
|
-
|
|
202
|
+
query: `
|
|
164
203
|
link: String
|
|
165
204
|
`,
|
|
166
|
-
|
|
205
|
+
mutation: `
|
|
167
206
|
linkUpdate(link: Date): String
|
|
168
207
|
`,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
208
|
+
subscription: ``,
|
|
209
|
+
type: ``
|
|
210
|
+
}
|
|
172
211
|
```
|
|
173
212
|
|
|
174
213
|
#### GraphQL Resolver Example (server/modules/link/resolver.js)
|
|
@@ -177,14 +216,14 @@ module.exports = {
|
|
|
177
216
|
let localLink = ''
|
|
178
217
|
|
|
179
218
|
module.exports = {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
219
|
+
link: () => {
|
|
220
|
+
return localLink
|
|
221
|
+
},
|
|
222
|
+
linkUpdate: ({ link }) => {
|
|
223
|
+
console.log('link', link)
|
|
224
|
+
localLink = link
|
|
225
|
+
return localLink
|
|
226
|
+
}
|
|
188
227
|
}
|
|
189
228
|
```
|
|
190
229
|
|
|
@@ -196,14 +235,12 @@ const { Kind } = require('graphql/language')
|
|
|
196
235
|
const { GraphQLScalarType } = require('graphql')
|
|
197
236
|
|
|
198
237
|
const customScalarDate = new GraphQLScalarType({
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
? parseInt(ast.value, 10)
|
|
205
|
-
: null
|
|
238
|
+
name: 'Date',
|
|
239
|
+
description: 'Date custom scalar type',
|
|
240
|
+
parseValue: (value) => moment(value).valueOf(),
|
|
241
|
+
serialize: (value) => moment(value).format('YYYY-MM-DD HH:mm:ss:SSS'),
|
|
242
|
+
parseLiteral: (ast) => (ast.kind === Kind.INT ? parseInt(ast.value, 10) : null)
|
|
206
243
|
})
|
|
207
244
|
|
|
208
245
|
module.exports = { Date: customScalarDate }
|
|
209
|
-
```
|
|
246
|
+
```
|
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
|
|