@kne/fastify-account 1.0.0-alpha.0
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 +1436 -0
- package/index.js +54 -0
- package/libs/controllers/account.js +189 -0
- package/libs/controllers/admin.js +153 -0
- package/libs/controllers/adminPermission.js +230 -0
- package/libs/controllers/adminRole.js +145 -0
- package/libs/controllers/adminTenant.js +461 -0
- package/libs/controllers/tenant.js +22 -0
- package/libs/controllers/user.js +12 -0
- package/libs/models/admin-role.js +19 -0
- package/libs/models/application.js +36 -0
- package/libs/models/login-log.js +15 -0
- package/libs/models/permission.js +53 -0
- package/libs/models/tenant-application.js +28 -0
- package/libs/models/tenant-org.js +30 -0
- package/libs/models/tenant-permission.js +28 -0
- package/libs/models/tenant-role-application.js +32 -0
- package/libs/models/tenant-role-permission.js +32 -0
- package/libs/models/tenant-role.js +27 -0
- package/libs/models/tenant-share-group-permission.js +22 -0
- package/libs/models/tenant-share-group.js +22 -0
- package/libs/models/tenant-source-user-share-group.js +22 -0
- package/libs/models/tenant-token.js +32 -0
- package/libs/models/tenant-user-org.js +22 -0
- package/libs/models/tenant-user-role.js +22 -0
- package/libs/models/tenant-user-share-group.js +22 -0
- package/libs/models/tenant-user.js +56 -0
- package/libs/models/tenant.js +38 -0
- package/libs/models/user-account.js +26 -0
- package/libs/models/user.js +50 -0
- package/libs/models/verification-code.js +26 -0
- package/libs/services/account.js +200 -0
- package/libs/services/admin.js +183 -0
- package/libs/services/permission.js +465 -0
- package/libs/services/tenant.js +576 -0
- package/libs/services/user.js +108 -0
- package/package.json +61 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
const dayjs = require('dayjs');
|
|
3
|
+
const bcrypt = require('bcryptjs');
|
|
4
|
+
|
|
5
|
+
function generateRandom6DigitNumber() {
|
|
6
|
+
const randomNumber = Math.random() * 1000000;
|
|
7
|
+
return Math.floor(randomNumber).toString().padStart(6, '0');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function userNameIsEmail(username) {
|
|
11
|
+
return /^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/.test(username);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = fp(async (fastify, options) => {
|
|
15
|
+
const login = async ({ username, password, ip }) => {
|
|
16
|
+
const isEmail = userNameIsEmail(username);
|
|
17
|
+
const user = await fastify.account.models.user.findOne({
|
|
18
|
+
where: Object.assign(
|
|
19
|
+
{},
|
|
20
|
+
isEmail
|
|
21
|
+
? {
|
|
22
|
+
email: username
|
|
23
|
+
}
|
|
24
|
+
: {
|
|
25
|
+
phone: username
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
status: {
|
|
29
|
+
[fastify.sequelize.Sequelize.Op.or]: [0, 1]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!user) {
|
|
36
|
+
throw new Error('用户名或密码错误');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await passwordAuthentication({ accountId: user.userAccountId, password });
|
|
40
|
+
|
|
41
|
+
await fastify.account.models.loginLog.create({
|
|
42
|
+
userId: user.id,
|
|
43
|
+
ip
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return fastify.jwt.sign({ payload: { id: user.id } });
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const passwordAuthentication = async ({ accountId, password }) => {
|
|
50
|
+
const userAccount = await fastify.account.models.userAccount.findByPk(accountId);
|
|
51
|
+
if (!userAccount) {
|
|
52
|
+
throw new Error('账号不存在');
|
|
53
|
+
}
|
|
54
|
+
const generatedHash = await bcrypt.hash(password + userAccount.salt, userAccount.salt);
|
|
55
|
+
if (userAccount.password !== generatedHash) {
|
|
56
|
+
throw new Error('用户名或密码错误');
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const passwordEncryption = async password => {
|
|
61
|
+
const salt = await bcrypt.genSalt(10);
|
|
62
|
+
const combinedString = password + salt;
|
|
63
|
+
const hash = await bcrypt.hash(combinedString, salt);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
password: hash,
|
|
67
|
+
salt
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const resetPassword = async ({ userId, password }) => {
|
|
72
|
+
const userInfo = await fastify.account.models.user.findByPk(userId);
|
|
73
|
+
if (!userInfo) {
|
|
74
|
+
throw new Error('用户不存在');
|
|
75
|
+
}
|
|
76
|
+
const account = await fastify.account.models.userAccount.create(
|
|
77
|
+
Object.assign({}, await passwordEncryption(password), {
|
|
78
|
+
belongToUserId: userId
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
await userInfo.update({ userAccountId: account.id });
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const register = async ({ avatar, nickname, gender, birthday, description, phone, email, code, password, status, invitationCode }) => {
|
|
86
|
+
const type = phone ? 0 : 1;
|
|
87
|
+
const verificationCode = await fastify.account.models.verificationCode.findOne({
|
|
88
|
+
where: {
|
|
89
|
+
name: type === 0 ? phone : email,
|
|
90
|
+
type,
|
|
91
|
+
code,
|
|
92
|
+
status: 1
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
if (!verificationCode) {
|
|
96
|
+
throw new Error('验证码不正确或者已经过期');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
verificationCode.status = 2;
|
|
100
|
+
await verificationCode.save();
|
|
101
|
+
|
|
102
|
+
return await fastify.account.services.user.addUser({
|
|
103
|
+
avatar,
|
|
104
|
+
nickname,
|
|
105
|
+
gender,
|
|
106
|
+
birthday,
|
|
107
|
+
description,
|
|
108
|
+
phone,
|
|
109
|
+
email,
|
|
110
|
+
password,
|
|
111
|
+
status
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const sendEmailCode = async ({ email }) => {
|
|
116
|
+
const code = generateRandom6DigitNumber();
|
|
117
|
+
|
|
118
|
+
// 这里写发送逻辑
|
|
119
|
+
|
|
120
|
+
await fastify.account.models.verificationCode.update(
|
|
121
|
+
{
|
|
122
|
+
status: 2
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
where: {
|
|
126
|
+
name: email,
|
|
127
|
+
type: 1,
|
|
128
|
+
status: 0
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
await fastify.account.models.verificationCode.create({
|
|
134
|
+
name: email,
|
|
135
|
+
type: 1,
|
|
136
|
+
code
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return code;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const verificationCodeValidate = async ({ name, type, code }) => {
|
|
143
|
+
const verificationCode = await fastify.account.models.verificationCode.findOne({
|
|
144
|
+
where: {
|
|
145
|
+
name,
|
|
146
|
+
type,
|
|
147
|
+
code,
|
|
148
|
+
status: {
|
|
149
|
+
[fastify.sequelize.Sequelize.Op.or]: [0, 1]
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const isPass = !!(verificationCode && dayjs().isBefore(dayjs(verificationCode.createdAt).add(10, 'minute')));
|
|
154
|
+
|
|
155
|
+
if (verificationCode) {
|
|
156
|
+
verificationCode.status = isPass ? 1 : 2;
|
|
157
|
+
await verificationCode.save();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return isPass;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const sendSMSCode = async ({ phone }) => {
|
|
164
|
+
const code = generateRandom6DigitNumber();
|
|
165
|
+
|
|
166
|
+
// 这里写发送逻辑
|
|
167
|
+
|
|
168
|
+
await fastify.account.models.verificationCode.update(
|
|
169
|
+
{
|
|
170
|
+
status: 2
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
where: {
|
|
174
|
+
name: phone,
|
|
175
|
+
type: 0,
|
|
176
|
+
status: 0
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
await fastify.account.models.verificationCode.create({
|
|
182
|
+
name: phone,
|
|
183
|
+
type: 0,
|
|
184
|
+
code
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return code;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
fastify.account.services.account = {
|
|
191
|
+
login,
|
|
192
|
+
register,
|
|
193
|
+
sendEmailCode,
|
|
194
|
+
sendSMSCode,
|
|
195
|
+
verificationCodeValidate,
|
|
196
|
+
passwordEncryption,
|
|
197
|
+
passwordAuthentication,
|
|
198
|
+
resetPassword
|
|
199
|
+
};
|
|
200
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
|
|
3
|
+
const ROLE = {
|
|
4
|
+
SuperAdmin: 'SuperAdmin',
|
|
5
|
+
TenantAdmin: 'TenantAdmin'
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
module.exports = fp(async (fastify, options) => {
|
|
9
|
+
const initSuperAdmin = async user => {
|
|
10
|
+
if ((await fastify.account.models.adminRole.count()) > 0) {
|
|
11
|
+
throw new Error('系统已经初始化完成,不能执行该操作');
|
|
12
|
+
}
|
|
13
|
+
await fastify.account.models.adminRole.create({
|
|
14
|
+
userId: user.id,
|
|
15
|
+
role: ROLE['SuperAdmin']
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const superAdminAuthenticate = async user => {
|
|
20
|
+
if (
|
|
21
|
+
(await fastify.account.models.adminRole.count({
|
|
22
|
+
where: {
|
|
23
|
+
userId: user.id,
|
|
24
|
+
role: ROLE['SuperAdmin']
|
|
25
|
+
}
|
|
26
|
+
})) === 0
|
|
27
|
+
) {
|
|
28
|
+
throw new Error('不能执行该操作,需要超级管理员权限');
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const addUser = async ({ avatar, nickname, phone, email, password, description }) => {
|
|
33
|
+
return await fastify.account.services.user.addUser({
|
|
34
|
+
avatar,
|
|
35
|
+
nickname,
|
|
36
|
+
phone,
|
|
37
|
+
email,
|
|
38
|
+
password,
|
|
39
|
+
description,
|
|
40
|
+
status: 1
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const setSuperAdmin = async targetUser => {
|
|
45
|
+
const user = await fastify.account.services.user.getUserInfo(targetUser);
|
|
46
|
+
if (
|
|
47
|
+
(await fastify.account.models.adminRole.count({
|
|
48
|
+
where: {
|
|
49
|
+
userId: user.id,
|
|
50
|
+
role: ROLE['SuperAdmin']
|
|
51
|
+
}
|
|
52
|
+
})) > 0
|
|
53
|
+
) {
|
|
54
|
+
throw new Error('当前用户已经是超级管理员');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await fastify.account.models.adminRole.create({
|
|
58
|
+
userId: user.id,
|
|
59
|
+
role: ROLE['SuperAdmin']
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const getAllUserList = async ({ filter, perPage, currentPage }) => {
|
|
64
|
+
const { count, rows } = await fastify.account.models.user.findAndCountAll({
|
|
65
|
+
include: [
|
|
66
|
+
{
|
|
67
|
+
attributes: ['role'],
|
|
68
|
+
model: fastify.account.models.adminRole
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
model: fastify.account.models.tenant
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
pageData: rows,
|
|
77
|
+
totalCount: count
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const getAllTenantList = async ({ filter, perPage, currentPage }) => {
|
|
82
|
+
const queryFilter = {};
|
|
83
|
+
if (filter?.name) {
|
|
84
|
+
queryFilter.name = {
|
|
85
|
+
[fastify.sequelize.Sequelize.Op.like]: `%${filter.name}%`
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (filter?.serviceStartTime) {
|
|
90
|
+
queryFilter.serviceStartTime = {
|
|
91
|
+
[fastify.sequelize.Sequelize.Op.gt]: filter.serviceStartTime
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (filter?.serviceEndTime) {
|
|
96
|
+
queryFilter.serviceEndTime = {
|
|
97
|
+
[fastify.sequelize.Sequelize.Op.lt]: filter.serviceEndTime
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { count, rows } = await fastify.account.models.tenant.findAndCountAll({
|
|
102
|
+
where: queryFilter,
|
|
103
|
+
offset: currentPage * (currentPage - 1),
|
|
104
|
+
limit: perPage
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const res = await fastify.account.models.tenantUser.findAll({
|
|
108
|
+
attributes: ['tenantId', fastify.sequelize.instance.fn('count', fastify.sequelize.instance.col('tenantId'))],
|
|
109
|
+
where: {
|
|
110
|
+
tenantId: {
|
|
111
|
+
[fastify.sequelize.Sequelize.Op.in]: rows.map(({ id }) => id)
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
group: 'tenantId'
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
pageData: rows,
|
|
119
|
+
totalCount: count
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const addTenant = async tenant => {
|
|
124
|
+
if (await fastify.account.models.tenant.count({ where: { name: tenant.name } })) {
|
|
125
|
+
throw new Error('租户名称不能重复');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const t = await fastify.sequelize.instance.transaction();
|
|
129
|
+
try {
|
|
130
|
+
const currentTenant = await fastify.account.models.tenant.create(tenant);
|
|
131
|
+
await fastify.account.models.tenantRole.create(
|
|
132
|
+
{
|
|
133
|
+
name: '系统默认角色',
|
|
134
|
+
tenantId: currentTenant.id,
|
|
135
|
+
description: '创建租户时自动生成,可以设置权限,不可更改删除,所有租户用户默认拥有该角色',
|
|
136
|
+
type: 1
|
|
137
|
+
},
|
|
138
|
+
{ transaction: t }
|
|
139
|
+
);
|
|
140
|
+
await fastify.account.models.tenantOrg.create(
|
|
141
|
+
{
|
|
142
|
+
name: '根组织',
|
|
143
|
+
tenantId: currentTenant.id
|
|
144
|
+
},
|
|
145
|
+
{ transaction: t }
|
|
146
|
+
);
|
|
147
|
+
await t.commit();
|
|
148
|
+
} catch (e) {
|
|
149
|
+
await t.rollback();
|
|
150
|
+
throw e;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const saveTenant = async tenant => {
|
|
155
|
+
const currentTenant = await fastify.account.models.tenant.findByPk(tenant.id);
|
|
156
|
+
if (!currentTenant) {
|
|
157
|
+
throw new Error('租户不存在,请刷新以后重试');
|
|
158
|
+
}
|
|
159
|
+
await currentTenant.update(tenant);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const generateTenantAdminVerifyCode = async () => {};
|
|
163
|
+
|
|
164
|
+
const verifyTenantAdmin = async () => {};
|
|
165
|
+
|
|
166
|
+
const resetUserPassword = async ({ userId, password }) => {
|
|
167
|
+
await fastify.account.services.account.resetPassword({ userId, password });
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
fastify.account.services.admin = {
|
|
171
|
+
initSuperAdmin,
|
|
172
|
+
setSuperAdmin,
|
|
173
|
+
getAllUserList,
|
|
174
|
+
getAllTenantList,
|
|
175
|
+
addTenant,
|
|
176
|
+
saveTenant,
|
|
177
|
+
superAdminAuthenticate,
|
|
178
|
+
generateTenantAdminVerifyCode,
|
|
179
|
+
verifyTenantAdmin,
|
|
180
|
+
resetUserPassword,
|
|
181
|
+
addUser
|
|
182
|
+
};
|
|
183
|
+
});
|