@kne/fastify-account 1.0.0-alpha.15 → 1.0.0-alpha.17
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 +2 -2
- package/libs/controllers/account.js +136 -5
- package/libs/models/verification-code.js +4 -2
- package/libs/services/account.js +63 -49
- package/libs/services/tenant-org.js +10 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ npm i --save @kne/fastify-account
|
|
|
22
22
|
### API
|
|
23
23
|
|
|
24
24
|
---
|
|
25
|
-
title: "@kne/fastify-account v1.0.0-alpha.
|
|
25
|
+
title: "@kne/fastify-account v1.0.0-alpha.16"
|
|
26
26
|
language_tabs:
|
|
27
27
|
- shell: Shell
|
|
28
28
|
- http: HTTP
|
|
@@ -42,7 +42,7 @@ headingLevel: 2
|
|
|
42
42
|
|
|
43
43
|
<!-- Generator: Widdershins v4.0.1 -->
|
|
44
44
|
|
|
45
|
-
<h1 id="-kne-fastify-account">@kne/fastify-account v1.0.0-alpha.
|
|
45
|
+
<h1 id="-kne-fastify-account">@kne/fastify-account v1.0.0-alpha.16</h1>
|
|
46
46
|
|
|
47
47
|
> Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.
|
|
48
48
|
|
|
@@ -6,7 +6,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
6
6
|
{
|
|
7
7
|
schema: {
|
|
8
8
|
tags: ['账号'],
|
|
9
|
-
summary: '
|
|
9
|
+
summary: '发送登录邮箱验证码',
|
|
10
10
|
body: {
|
|
11
11
|
type: 'object',
|
|
12
12
|
required: ['email'],
|
|
@@ -32,7 +32,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
32
32
|
},
|
|
33
33
|
async request => {
|
|
34
34
|
const { email } = request.body;
|
|
35
|
-
const code = await services.account.
|
|
35
|
+
const code = await services.account.sendVerificationCode({ name: email, type: 0, messageType: 1 });
|
|
36
36
|
return options.isTest ? { code } : {};
|
|
37
37
|
}
|
|
38
38
|
);
|
|
@@ -42,7 +42,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
42
42
|
{
|
|
43
43
|
schema: {
|
|
44
44
|
tags: ['账号'],
|
|
45
|
-
summary: '
|
|
45
|
+
summary: '发送登录短信验证码',
|
|
46
46
|
body: {
|
|
47
47
|
type: 'object',
|
|
48
48
|
required: ['phone'],
|
|
@@ -68,7 +68,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
68
68
|
},
|
|
69
69
|
async request => {
|
|
70
70
|
const { phone } = request.body;
|
|
71
|
-
const code = await services.account.
|
|
71
|
+
const code = await services.account.sendVerificationCode({ name: phone, type: 0, messageType: 0 });
|
|
72
72
|
return options.isTest ? { code } : {};
|
|
73
73
|
}
|
|
74
74
|
);
|
|
@@ -84,7 +84,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
84
84
|
required: ['name', 'type', 'code'],
|
|
85
85
|
properties: {
|
|
86
86
|
name: { type: 'string', description: '被验证的账号,手机或邮箱' },
|
|
87
|
-
type: { type: 'number', description: '0
|
|
87
|
+
type: { type: 'number', description: '0:注册,2:登录,4:验证租户管理员,5:忘记密码' },
|
|
88
88
|
code: { type: 'string', description: '接受到的验证码' }
|
|
89
89
|
}
|
|
90
90
|
},
|
|
@@ -253,4 +253,135 @@ module.exports = fp(async (fastify, options) => {
|
|
|
253
253
|
return { token, currentTenantId: user.currentTenantId };
|
|
254
254
|
}
|
|
255
255
|
);
|
|
256
|
+
|
|
257
|
+
fastify.post(
|
|
258
|
+
`${options.prefix}/modifyPassword`,
|
|
259
|
+
{
|
|
260
|
+
schema: {
|
|
261
|
+
tags: ['账号'],
|
|
262
|
+
summary: '新用户重置新密码',
|
|
263
|
+
body: {
|
|
264
|
+
oneOf: [
|
|
265
|
+
{
|
|
266
|
+
type: 'object',
|
|
267
|
+
required: ['email', 'newPwd', 'oldPwd'],
|
|
268
|
+
properties: {
|
|
269
|
+
email: { type: 'string', description: '邮箱' },
|
|
270
|
+
newPwd: { type: 'string', description: '新密码' },
|
|
271
|
+
oldPwd: { type: 'string', description: '原密码' }
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
type: 'object',
|
|
276
|
+
required: ['phone', 'newPwd', 'oldPwd'],
|
|
277
|
+
properties: {
|
|
278
|
+
phone: { type: 'string', description: '手机号' },
|
|
279
|
+
newPwd: { type: 'string', description: '新密码' },
|
|
280
|
+
oldPwd: { type: 'string', description: '原密码' }
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
async request => {
|
|
288
|
+
await services.account.modifyPassword(request.body);
|
|
289
|
+
return {};
|
|
290
|
+
}
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
fastify.post(
|
|
294
|
+
`${options.prefix}/resetPassword`,
|
|
295
|
+
{
|
|
296
|
+
schema: {
|
|
297
|
+
tags: ['账号'],
|
|
298
|
+
summary: '用户重置密码',
|
|
299
|
+
body: {
|
|
300
|
+
oneOf: [
|
|
301
|
+
{
|
|
302
|
+
type: 'object',
|
|
303
|
+
required: ['email', 'newPwd', 'token'],
|
|
304
|
+
properties: {
|
|
305
|
+
email: { type: 'string', description: '邮箱' },
|
|
306
|
+
newPwd: { type: 'string', description: '新密码' },
|
|
307
|
+
token: { type: 'string', description: '验证token' }
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
type: 'object',
|
|
312
|
+
required: ['phone', 'newPwd'],
|
|
313
|
+
properties: {
|
|
314
|
+
phone: { type: 'string', description: '手机号' },
|
|
315
|
+
newPwd: { type: 'string', description: '新密码' },
|
|
316
|
+
token: { type: 'string', description: '验证token' }
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
async request => {
|
|
324
|
+
await services.account.resetPassword({
|
|
325
|
+
email: request.body.email,
|
|
326
|
+
password: request.body.newPwd,
|
|
327
|
+
token: request.body.token
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
return {};
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
fastify.post(
|
|
335
|
+
`${options.prefix}/forgetPwd`,
|
|
336
|
+
{
|
|
337
|
+
schema: {
|
|
338
|
+
tags: ['账号'],
|
|
339
|
+
summary: '忘记密码',
|
|
340
|
+
body: {
|
|
341
|
+
oneOf: [
|
|
342
|
+
{
|
|
343
|
+
type: 'object',
|
|
344
|
+
required: ['email'],
|
|
345
|
+
properties: {
|
|
346
|
+
email: { type: 'string', description: '邮箱' }
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
type: 'object',
|
|
351
|
+
required: ['phone'],
|
|
352
|
+
properties: {
|
|
353
|
+
phone: { type: 'string', description: '手机号' }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
]
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
async request => {
|
|
361
|
+
const name = request.body.email || request.body.phone;
|
|
362
|
+
const token = await services.account.sendJWTVerificationCode({ name, type: 5, messageType: 3 });
|
|
363
|
+
return options.isTest ? { token } : {};
|
|
364
|
+
}
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
fastify.post(
|
|
368
|
+
`${options.prefix}/parseResetToken`,
|
|
369
|
+
{
|
|
370
|
+
schema: {
|
|
371
|
+
tags: ['账号'],
|
|
372
|
+
summary: '通过token获取name',
|
|
373
|
+
body: {
|
|
374
|
+
type: 'object',
|
|
375
|
+
required: ['token'],
|
|
376
|
+
properties: {
|
|
377
|
+
token: { type: 'string' }
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
async request => {
|
|
383
|
+
const { name } = await services.account.verificationJWTCodeValidate(request.body);
|
|
384
|
+
return { name };
|
|
385
|
+
}
|
|
386
|
+
);
|
|
256
387
|
});
|
|
@@ -7,7 +7,8 @@ module.exports = ({ DataTypes }) => {
|
|
|
7
7
|
},
|
|
8
8
|
type: {
|
|
9
9
|
type: DataTypes.INTEGER,
|
|
10
|
-
allowNull: false
|
|
10
|
+
allowNull: false,
|
|
11
|
+
comment: '0:注册,2:登录,4:验证租户管理员,5:忘记密码'
|
|
11
12
|
},
|
|
12
13
|
code: {
|
|
13
14
|
type: DataTypes.STRING,
|
|
@@ -15,7 +16,8 @@ module.exports = ({ DataTypes }) => {
|
|
|
15
16
|
},
|
|
16
17
|
status: {
|
|
17
18
|
type: DataTypes.INTEGER,
|
|
18
|
-
defaultValue: 0
|
|
19
|
+
defaultValue: 0,
|
|
20
|
+
comment: '0:未验证,1:已验证,2:已过期'
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
};
|
package/libs/services/account.js
CHANGED
|
@@ -51,6 +51,34 @@ module.exports = fp(async (fastify, options) => {
|
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
const modifyPassword = async ({ email, phone, oldPwd, newPwd }) => {
|
|
55
|
+
const user = await models.user.findOne({
|
|
56
|
+
where: Object.assign(
|
|
57
|
+
{},
|
|
58
|
+
email
|
|
59
|
+
? {
|
|
60
|
+
email
|
|
61
|
+
}
|
|
62
|
+
: {
|
|
63
|
+
phone
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
status: 1
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
});
|
|
70
|
+
if (!user) {
|
|
71
|
+
throw new Error('新用户密码只能初始化一次');
|
|
72
|
+
}
|
|
73
|
+
if (oldPwd === newPwd) {
|
|
74
|
+
throw new Error('重置密码不能和初始化密码相同');
|
|
75
|
+
}
|
|
76
|
+
await passwordAuthentication({ accountId: user.userAccountId, password: oldPwd });
|
|
77
|
+
await resetPassword({ userId: user.uuid, password: newPwd });
|
|
78
|
+
user.status = 0;
|
|
79
|
+
await user.save();
|
|
80
|
+
};
|
|
81
|
+
|
|
54
82
|
const passwordAuthentication = async ({ accountId, password }) => {
|
|
55
83
|
const userAccount = await models.userAccount.findOne({
|
|
56
84
|
where: {
|
|
@@ -83,16 +111,20 @@ module.exports = fp(async (fastify, options) => {
|
|
|
83
111
|
return hash.digest('hex');
|
|
84
112
|
};
|
|
85
113
|
|
|
86
|
-
const resetPassword = async ({
|
|
114
|
+
const resetPassword = async ({ password, token }) => {
|
|
115
|
+
const { name } = await verificationJWTCodeValidate({ token });
|
|
116
|
+
|
|
117
|
+
const isEmail = userNameIsEmail(name);
|
|
118
|
+
|
|
87
119
|
const userInfo = await models.user.findOne({
|
|
88
|
-
where: {
|
|
120
|
+
where: isEmail ? { email: name } : { phone: name }
|
|
89
121
|
});
|
|
90
122
|
if (!userInfo) {
|
|
91
123
|
throw new Error('用户不存在');
|
|
92
124
|
}
|
|
93
125
|
const account = await models.userAccount.create(
|
|
94
126
|
Object.assign({}, await passwordEncryption(password), {
|
|
95
|
-
belongToUserId:
|
|
127
|
+
belongToUserId: userInfo.uuid
|
|
96
128
|
})
|
|
97
129
|
);
|
|
98
130
|
|
|
@@ -101,21 +133,11 @@ module.exports = fp(async (fastify, options) => {
|
|
|
101
133
|
|
|
102
134
|
const register = async ({ avatar, nickname, gender, birthday, description, phone, email, code, password, status, invitationCode }) => {
|
|
103
135
|
const type = phone ? 0 : 1;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
name: type === 0 ? phone : email,
|
|
107
|
-
type,
|
|
108
|
-
code,
|
|
109
|
-
status: 1
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
if (!verificationCode) {
|
|
136
|
+
|
|
137
|
+
if (!(await verificationCodeValidate({ name: type === 0 ? phone : email, type, code }))) {
|
|
113
138
|
throw new Error('验证码不正确或者已经过期');
|
|
114
139
|
}
|
|
115
140
|
|
|
116
|
-
verificationCode.status = 2;
|
|
117
|
-
await verificationCode.save();
|
|
118
|
-
|
|
119
141
|
return await services.user.addUser({
|
|
120
142
|
avatar,
|
|
121
143
|
nickname,
|
|
@@ -129,30 +151,31 @@ module.exports = fp(async (fastify, options) => {
|
|
|
129
151
|
});
|
|
130
152
|
};
|
|
131
153
|
|
|
132
|
-
const
|
|
154
|
+
const generateVerificationCode = async ({ name, type }) => {
|
|
133
155
|
const code = generateRandom6DigitNumber();
|
|
134
|
-
|
|
135
|
-
// 这里写发送逻辑
|
|
136
|
-
|
|
137
156
|
await models.verificationCode.update(
|
|
138
157
|
{
|
|
139
158
|
status: 2
|
|
140
159
|
},
|
|
141
160
|
{
|
|
142
161
|
where: {
|
|
143
|
-
name
|
|
144
|
-
type
|
|
162
|
+
name,
|
|
163
|
+
type,
|
|
145
164
|
status: 0
|
|
146
165
|
}
|
|
147
166
|
}
|
|
148
167
|
);
|
|
149
|
-
|
|
150
168
|
await models.verificationCode.create({
|
|
151
|
-
name
|
|
152
|
-
type
|
|
169
|
+
name,
|
|
170
|
+
type,
|
|
153
171
|
code
|
|
154
172
|
});
|
|
173
|
+
return code;
|
|
174
|
+
};
|
|
155
175
|
|
|
176
|
+
const sendVerificationCode = async ({ name, type, messageType }) => {
|
|
177
|
+
const code = await generateVerificationCode({ name, type });
|
|
178
|
+
// 这里写发送逻辑
|
|
156
179
|
return code;
|
|
157
180
|
};
|
|
158
181
|
|
|
@@ -177,40 +200,31 @@ module.exports = fp(async (fastify, options) => {
|
|
|
177
200
|
return isPass;
|
|
178
201
|
};
|
|
179
202
|
|
|
180
|
-
const
|
|
181
|
-
const code =
|
|
182
|
-
|
|
203
|
+
const sendJWTVerificationCode = async ({ name, type, messageType }) => {
|
|
204
|
+
const code = await generateVerificationCode({ name, type });
|
|
205
|
+
const token = fastify.jwt.sign({ name, type, code });
|
|
183
206
|
// 这里写发送逻辑
|
|
207
|
+
return token;
|
|
208
|
+
};
|
|
184
209
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
name: phone,
|
|
192
|
-
type: 0,
|
|
193
|
-
status: 0
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
await models.verificationCode.create({
|
|
199
|
-
name: phone,
|
|
200
|
-
type: 0,
|
|
201
|
-
code
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
return code;
|
|
210
|
+
const verificationJWTCodeValidate = async ({ token }) => {
|
|
211
|
+
const { iat, name, type, code } = fastify.jwt.decode(token);
|
|
212
|
+
if (!(await verificationCodeValidate({ name, type, code }))) {
|
|
213
|
+
throw new Error('验证码不正确或者已经过期');
|
|
214
|
+
}
|
|
215
|
+
return { name, type, code };
|
|
205
216
|
};
|
|
206
217
|
|
|
207
218
|
services.account = {
|
|
208
219
|
md5,
|
|
209
220
|
login,
|
|
221
|
+
modifyPassword,
|
|
210
222
|
register,
|
|
211
|
-
|
|
212
|
-
|
|
223
|
+
generateVerificationCode,
|
|
224
|
+
sendVerificationCode,
|
|
213
225
|
verificationCodeValidate,
|
|
226
|
+
sendJWTVerificationCode,
|
|
227
|
+
verificationJWTCodeValidate,
|
|
214
228
|
passwordEncryption,
|
|
215
229
|
passwordAuthentication,
|
|
216
230
|
resetPassword
|
|
@@ -17,8 +17,16 @@ module.exports = fp(async (fastify, options) => {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const addTenantOrg = async org => {
|
|
20
|
-
if (
|
|
21
|
-
|
|
20
|
+
if (
|
|
21
|
+
await models.tenantOrg.count({
|
|
22
|
+
where: {
|
|
23
|
+
name: org.name,
|
|
24
|
+
pid: org.pid,
|
|
25
|
+
tenantId: org.tenantId
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
) {
|
|
29
|
+
throw new Error('组织名称在同一父组织下有重复');
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
return await models.tenantOrg.create({
|