@befly-addon/admin 1.0.2 → 1.0.4
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/apis/admin/del.ts +35 -0
- package/apis/admin/info.ts +50 -0
- package/apis/admin/ins.ts +60 -0
- package/apis/admin/list.ts +20 -0
- package/apis/admin/roleDetail.ts +35 -0
- package/apis/admin/roleSave.ts +40 -0
- package/apis/admin/upd.ts +51 -0
- package/apis/api/all.ts +37 -0
- package/apis/auth/login.ts +76 -0
- package/apis/auth/logout.ts +23 -0
- package/apis/auth/register.ts +48 -0
- package/apis/auth/sendSmsCode.ts +35 -0
- package/apis/dashboard/addonList.ts +47 -0
- package/apis/dashboard/changelog.ts +37 -0
- package/apis/dashboard/configStatus.ts +54 -0
- package/apis/dashboard/environmentInfo.ts +46 -0
- package/apis/dashboard/performanceMetrics.ts +23 -0
- package/apis/dashboard/permissionStats.ts +31 -0
- package/apis/dashboard/serviceStatus.ts +81 -0
- package/apis/dashboard/systemInfo.ts +25 -0
- package/apis/dashboard/systemOverview.ts +32 -0
- package/apis/dashboard/systemResources.ts +119 -0
- package/apis/dict/all.ts +25 -0
- package/apis/dict/del.ts +19 -0
- package/apis/dict/detail.ts +21 -0
- package/apis/dict/ins.ts +27 -0
- package/apis/dict/list.ts +18 -0
- package/apis/dict/upd.ts +31 -0
- package/apis/menu/all.ts +68 -0
- package/apis/menu/del.ts +37 -0
- package/apis/menu/ins.ts +20 -0
- package/apis/menu/list.ts +21 -0
- package/apis/menu/upd.ts +29 -0
- package/apis/role/apiDetail.ts +30 -0
- package/apis/role/apiSave.ts +41 -0
- package/apis/role/del.ts +44 -0
- package/apis/role/detail.ts +24 -0
- package/apis/role/ins.ts +39 -0
- package/apis/role/list.ts +14 -0
- package/apis/role/menuDetail.ts +30 -0
- package/apis/role/menuSave.ts +38 -0
- package/apis/role/save.ts +44 -0
- package/apis/role/upd.ts +40 -0
- package/package.json +2 -2
- package/util.ts +1 -1
- package/apis/.gitkeep +0 -0
- package/checks/.gitkeep +0 -0
- package/config/.gitkeep +0 -0
- package/plugins/.gitkeep +0 -0
- package/scripts/.gitkeep +0 -0
- package/tables/.gitkeep +0 -0
- package/types/index.ts +0 -44
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 删除管理员
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, No } from 'befly';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: '删除管理员',
|
|
9
|
+
fields: {},
|
|
10
|
+
required: ['id'],
|
|
11
|
+
handler: async (befly, ctx) => {
|
|
12
|
+
// 检查管理员是否存在
|
|
13
|
+
const admin = await befly.db.getOne({
|
|
14
|
+
table: 'core_admin',
|
|
15
|
+
where: { id: ctx.body.id }
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (!admin) {
|
|
19
|
+
return No('管理员不存在');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 不能删除 dev 角色的管理员
|
|
23
|
+
if (admin.roleCode === 'dev') {
|
|
24
|
+
return No('不能删除开发者账号');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 删除管理员
|
|
28
|
+
await befly.db.delData({
|
|
29
|
+
table: 'core_admin',
|
|
30
|
+
where: { id: ctx.body.id }
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return Yes('删除成功');
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取用户信息接口
|
|
3
|
+
*
|
|
4
|
+
* @returns 返回 admin 表的字段(不含 password)以及关联的角色信息(小驼峰格式):
|
|
5
|
+
* - id, name, email, phone, roleId, roleCode, state
|
|
6
|
+
* - lastLoginTime, lastLoginIp
|
|
7
|
+
* - createdAt, updatedAt
|
|
8
|
+
* - role: 角色完整信息(包含 menus 和 apis 权限)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Yes, No } from 'befly';
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
name: '获取用户信息',
|
|
15
|
+
handler: async (befly, ctx) => {
|
|
16
|
+
// 从 JWT token 中获取用户ID
|
|
17
|
+
const userId = ctx.user?.id;
|
|
18
|
+
|
|
19
|
+
if (!userId) {
|
|
20
|
+
return No('未授权');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 查询用户信息(框架自动转换为小驼峰)
|
|
24
|
+
const admin = await befly.db.getOne({
|
|
25
|
+
table: 'core_admin',
|
|
26
|
+
where: { id: userId }
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!admin) {
|
|
30
|
+
return No('用户不存在');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 查询角色信息(使用 roleCode 而非 roleId,框架自动转换为小驼峰)
|
|
34
|
+
let roleInfo = null;
|
|
35
|
+
if (admin.roleCode) {
|
|
36
|
+
roleInfo = await befly.db.getOne({
|
|
37
|
+
table: 'core_role',
|
|
38
|
+
where: { code: admin.roleCode }
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 返回用户信息(不包含密码,字段已是小驼峰格式)
|
|
43
|
+
const { password: _, ...userWithoutPassword } = admin;
|
|
44
|
+
|
|
45
|
+
return Yes('获取成功', {
|
|
46
|
+
...userWithoutPassword,
|
|
47
|
+
role: roleInfo
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 添加管理员
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, No, Cipher } from 'befly';
|
|
6
|
+
import adminTable from '../../tables/admin.json';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: '添加管理员',
|
|
10
|
+
fields: adminTable,
|
|
11
|
+
required: ['username', 'email', 'password'],
|
|
12
|
+
handler: async (befly, ctx) => {
|
|
13
|
+
// 检查用户名是否已存在
|
|
14
|
+
if (ctx.body.username) {
|
|
15
|
+
const existingByUsername = await befly.db.getOne({
|
|
16
|
+
table: 'core_admin',
|
|
17
|
+
where: { username: ctx.body.username }
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (existingByUsername) {
|
|
21
|
+
return No('用户名已被使用');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 检查邮箱是否已存在
|
|
26
|
+
const existingByEmail = await befly.db.getOne({
|
|
27
|
+
table: 'core_admin',
|
|
28
|
+
where: { email: ctx.body.email }
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (existingByEmail) {
|
|
32
|
+
return No('邮箱已被使用');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 加密密码
|
|
36
|
+
const hashedPassword = await Cipher.hashPassword(ctx.body.password);
|
|
37
|
+
|
|
38
|
+
// 创建管理员
|
|
39
|
+
const adminId = await befly.db.insData({
|
|
40
|
+
table: 'core_admin',
|
|
41
|
+
data: {
|
|
42
|
+
username: ctx.body.username,
|
|
43
|
+
email: ctx.body.email,
|
|
44
|
+
password: hashedPassword,
|
|
45
|
+
name: ctx.body.name,
|
|
46
|
+
nickname: ctx.body.nickname,
|
|
47
|
+
phone: ctx.body.phone,
|
|
48
|
+
roleId: ctx.body.roleId || 0,
|
|
49
|
+
roleCode: ctx.body.roleCode || '',
|
|
50
|
+
roleType: ctx.body.roleType || 'user'
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return Yes('添加成功', {
|
|
55
|
+
id: adminId,
|
|
56
|
+
username: ctx.body.username,
|
|
57
|
+
email: ctx.body.email
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取管理员列表
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes } from 'befly';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: '获取管理员列表',
|
|
9
|
+
handler: async (befly, ctx) => {
|
|
10
|
+
// 查询所有管理员(框架自动排除password字段,自动转换字段名为小驼峰)
|
|
11
|
+
const result = await befly.db.getList({
|
|
12
|
+
table: 'core_admin',
|
|
13
|
+
page: ctx.body.page || 1,
|
|
14
|
+
limit: ctx.body.limit || 10,
|
|
15
|
+
orderBy: ['createdAt#DESC']
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return Yes('获取成功', result);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取管理员的角色(单角色模式)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, No } from 'befly';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: '获取管理员角色',
|
|
9
|
+
handler: async (befly, ctx) => {
|
|
10
|
+
// 查询管理员信息(框架自动转换为小驼峰)
|
|
11
|
+
const admin = await befly.db.getOne({
|
|
12
|
+
table: 'core_admin',
|
|
13
|
+
where: { id: ctx.body.adminId }
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
if (!admin) {
|
|
17
|
+
return No('管理员不存在');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 如果有角色编码,查询角色详细信息(使用 roleCode 而非 roleId)
|
|
21
|
+
let roleInfo = null;
|
|
22
|
+
if (admin.roleCode) {
|
|
23
|
+
roleInfo = await befly.db.getOne({
|
|
24
|
+
table: 'core_role',
|
|
25
|
+
where: { code: admin.roleCode }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Yes('操作成功', {
|
|
30
|
+
roleId: admin.roleId,
|
|
31
|
+
roleCode: admin.roleCode,
|
|
32
|
+
role: roleInfo
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 保存管理员的角色(单角色模式)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, No } from 'befly';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: '保存管理员角色',
|
|
9
|
+
fields: {
|
|
10
|
+
adminId: '@id',
|
|
11
|
+
roleCode: '角色编码|string|2|50|null|1|^[a-zA-Z0-9_]+$'
|
|
12
|
+
},
|
|
13
|
+
handler: async (befly, ctx) => {
|
|
14
|
+
// 查询角色是否存在(使用 roleCode 而非 roleId)
|
|
15
|
+
const role = await befly.db.getOne({
|
|
16
|
+
table: 'core_role',
|
|
17
|
+
where: { code: ctx.body.roleCode }
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (!role) {
|
|
21
|
+
return No('角色不存在');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 根据角色编码判断角色类型(硬编码规则)
|
|
25
|
+
const roleType = role.code === 'dev' || role.code === 'admin' ? 'admin' : 'user';
|
|
26
|
+
|
|
27
|
+
// 更新管理员的角色ID、角色编码和角色类型
|
|
28
|
+
await befly.db.updData({
|
|
29
|
+
table: 'core_admin',
|
|
30
|
+
where: { id: ctx.body.adminId },
|
|
31
|
+
data: {
|
|
32
|
+
roleId: role.id,
|
|
33
|
+
roleCode: role.code,
|
|
34
|
+
roleType: roleType
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return Yes('操作成功');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 更新管理员信息
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, No } from 'befly';
|
|
6
|
+
import adminTable from '../../tables/admin.json';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: '更新管理员',
|
|
10
|
+
fields: {
|
|
11
|
+
name: adminTable.name,
|
|
12
|
+
nickname: adminTable.nickname,
|
|
13
|
+
email: adminTable.email,
|
|
14
|
+
phone: adminTable.phone
|
|
15
|
+
},
|
|
16
|
+
required: ['id'],
|
|
17
|
+
handler: async (befly, ctx) => {
|
|
18
|
+
const { id, ...updateData } = ctx.body;
|
|
19
|
+
|
|
20
|
+
// 检查管理员是否存在
|
|
21
|
+
const admin = await befly.db.getOne({
|
|
22
|
+
table: 'core_admin',
|
|
23
|
+
where: { id }
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!admin) {
|
|
27
|
+
return No('管理员不存在');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 如果更新邮箱,检查是否重复
|
|
31
|
+
if (updateData.email && updateData.email !== admin.email) {
|
|
32
|
+
const existingAdmin = await befly.db.getOne({
|
|
33
|
+
table: 'core_admin',
|
|
34
|
+
where: { email: updateData.email }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (existingAdmin) {
|
|
38
|
+
return No('邮箱已被使用');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 更新管理员信息
|
|
43
|
+
await befly.db.updData({
|
|
44
|
+
table: 'core_admin',
|
|
45
|
+
data: updateData,
|
|
46
|
+
where: { id }
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return Yes('更新成功');
|
|
50
|
+
}
|
|
51
|
+
};
|
package/apis/api/all.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取所有接口列表
|
|
3
|
+
* 说明:用于角色权限配置,返回所有接口信息
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Yes, No } from 'befly';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: '获取所有接口',
|
|
10
|
+
handler: async (befly, ctx) => {
|
|
11
|
+
try {
|
|
12
|
+
// 从 Redis 缓存读取所有接口
|
|
13
|
+
let allApis = await befly.redis.getObject<any[]>('apis:all');
|
|
14
|
+
|
|
15
|
+
// 如果缓存不存在,从数据库查询
|
|
16
|
+
if (!allApis || allApis.length === 0) {
|
|
17
|
+
befly.logger.info('接口缓存未命中,从数据库查询');
|
|
18
|
+
allApis = await befly.db.getAll({
|
|
19
|
+
table: 'core_api',
|
|
20
|
+
fields: ['id', 'name', 'path', 'method', 'description', 'addonName', 'addonTitle'],
|
|
21
|
+
orderBy: ['addonName#ASC', 'path#ASC']
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// 回写缓存
|
|
25
|
+
if (allApis.length > 0) {
|
|
26
|
+
await befly.redis.setObject('apis:all', allApis);
|
|
27
|
+
}
|
|
28
|
+
return Yes('操作成功', { lists: allApis, from: '来自数据库' });
|
|
29
|
+
} else {
|
|
30
|
+
return Yes('操作成功', { lists: allApis, from: '来自缓存' });
|
|
31
|
+
}
|
|
32
|
+
} catch (error: any) {
|
|
33
|
+
befly.logger.error('获取接口列表失败:', error);
|
|
34
|
+
return No('获取接口列表失败');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 管理员登录接口
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, No, Cipher, Jwt } from 'befly';
|
|
6
|
+
import adminTable from '../../tables/admin.json';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: '管理员登录',
|
|
10
|
+
auth: false,
|
|
11
|
+
fields: {
|
|
12
|
+
account: '账号|string|3|100|null|1|null',
|
|
13
|
+
password: adminTable.password
|
|
14
|
+
},
|
|
15
|
+
required: ['account', 'password'],
|
|
16
|
+
handler: async (befly, ctx) => {
|
|
17
|
+
// 查询管理员(account 匹配 username 或 email)
|
|
18
|
+
const admin = await befly.db.getOne({
|
|
19
|
+
table: 'core_admin',
|
|
20
|
+
where: {
|
|
21
|
+
$or: [{ username: ctx.body.account }, { email: ctx.body.account }]
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!admin) {
|
|
26
|
+
return No('账号或密码错误1');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 验证密码
|
|
30
|
+
try {
|
|
31
|
+
const isValid = await Cipher.verifyPassword(ctx.body.password, admin.password);
|
|
32
|
+
if (!isValid) {
|
|
33
|
+
return No('账号或密码错误2');
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
befly.logger.error('密码验证失败', error);
|
|
37
|
+
return No('密码格式错误,请重新设置密码');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 检查账号状态(state=1 表示正常,state=2 表示禁用)
|
|
41
|
+
if (admin.state === 2) {
|
|
42
|
+
return No('账号已被禁用');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 更新最后登录信息
|
|
46
|
+
await befly.db.updData({
|
|
47
|
+
table: 'core_admin',
|
|
48
|
+
where: { id: admin.id },
|
|
49
|
+
data: {
|
|
50
|
+
lastLoginTime: Date.now(),
|
|
51
|
+
lastLoginIp: ctx.ip || 'unknown'
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 生成 JWT Token(包含核心身份信息)
|
|
56
|
+
const token = await Jwt.sign(
|
|
57
|
+
{
|
|
58
|
+
id: admin.id,
|
|
59
|
+
nickname: admin.nickname,
|
|
60
|
+
roleCode: admin.roleCode,
|
|
61
|
+
roleType: admin.roleType
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
expiresIn: '7d'
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// 返回用户信息(不包含密码)
|
|
69
|
+
const { password: _, ...userWithoutPassword } = admin;
|
|
70
|
+
|
|
71
|
+
return Yes('登录成功', {
|
|
72
|
+
token,
|
|
73
|
+
userInfo: userWithoutPassword
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 退出登录接口
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes } from 'befly';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: '退出登录',
|
|
9
|
+
handler: async (befly, ctx) => {
|
|
10
|
+
// JWT token 是无状态的,前端删除 token 即可
|
|
11
|
+
// 如果需要实现 token 黑名单,可以在这里将 token 加入 Redis 黑名单
|
|
12
|
+
|
|
13
|
+
const token = ctx.headers.authorization?.replace('Bearer ', '');
|
|
14
|
+
|
|
15
|
+
if (token && befly.redis) {
|
|
16
|
+
// 将 token 加入黑名单,有效期设置为 token 的剩余有效期
|
|
17
|
+
const key = `token_blacklist:${token}`;
|
|
18
|
+
await befly.redis.set(key, '1', 'EX', 7 * 24 * 60 * 60); // 7天
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return Yes('退出成功');
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 管理员注册接口
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, No, Cipher } from 'befly';
|
|
6
|
+
import adminTable from '../../tables/admin.json';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: '管理员注册',
|
|
10
|
+
auth: false,
|
|
11
|
+
fields: {
|
|
12
|
+
name: adminTable.name,
|
|
13
|
+
email: adminTable.email,
|
|
14
|
+
password: adminTable.password
|
|
15
|
+
},
|
|
16
|
+
required: ['name', 'email', 'password'],
|
|
17
|
+
handler: async (befly, ctx) => {
|
|
18
|
+
// 检查邮箱是否已存在
|
|
19
|
+
const existingAdmin = await befly.db.getOne({
|
|
20
|
+
table: 'core_admin',
|
|
21
|
+
where: { email: ctx.body.email }
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (existingAdmin) {
|
|
25
|
+
return No('该邮箱已被注册');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 加密密码
|
|
29
|
+
const hashedPassword = await Cipher.hashPassword(ctx.body.password);
|
|
30
|
+
|
|
31
|
+
// 创建管理员
|
|
32
|
+
const adminId = await befly.db.insData({
|
|
33
|
+
table: 'core_admin',
|
|
34
|
+
data: {
|
|
35
|
+
name: ctx.body.name,
|
|
36
|
+
email: ctx.body.email,
|
|
37
|
+
password: hashedPassword,
|
|
38
|
+
role: 'user' // 默认为普通用户,state 由框架自动设置为 1
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return Yes('注册成功', {
|
|
43
|
+
id: adminId,
|
|
44
|
+
name: ctx.body.name,
|
|
45
|
+
email: ctx.body.email
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 发送短信验证码接口
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, Env } from 'befly';
|
|
6
|
+
import adminTable from '../../tables/admin.json';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: '发送短信验证码',
|
|
10
|
+
auth: false,
|
|
11
|
+
fields: {
|
|
12
|
+
phone: adminTable.phone
|
|
13
|
+
},
|
|
14
|
+
required: ['phone'],
|
|
15
|
+
handler: async (befly, ctx) => {
|
|
16
|
+
// 生成6位数验证码
|
|
17
|
+
const code = Math.floor(100000 + Math.random() * 900000).toString();
|
|
18
|
+
|
|
19
|
+
// TODO: 调用短信服务发送验证码
|
|
20
|
+
// 这里需要集成实际的短信服务提供商(如阿里云、腾讯云等)
|
|
21
|
+
// await smsService.send(phone, code);
|
|
22
|
+
|
|
23
|
+
// 将验证码存储到 Redis 中,有效期5分钟
|
|
24
|
+
// 如果项目没有 Redis,也可以存储到数据库中
|
|
25
|
+
if (befly.redis) {
|
|
26
|
+
const key = `sms_code:${ctx.body.phone}`;
|
|
27
|
+
await befly.redis.set(key, code, 'EX', 300); // 5分钟过期
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 开发环境下返回验证码(生产环境应该删除)
|
|
31
|
+
const isDev = Env.NODE_ENV === 'development';
|
|
32
|
+
|
|
33
|
+
return Yes('验证码已发送', isDev ? { code } : null);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取插件列表
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes, Addon } from 'befly';
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: '获取插件列表',
|
|
10
|
+
handler: async (befly, ctx) => {
|
|
11
|
+
const addonList: Array<{ name: string; title: string; version: string; description: string; enabled: boolean }> = [];
|
|
12
|
+
|
|
13
|
+
// 使用 Addon.scan() 扫描所有 addon
|
|
14
|
+
const addonNames = Addon.scan();
|
|
15
|
+
|
|
16
|
+
for (const addonName of addonNames) {
|
|
17
|
+
// addonName 格式: admin, demo 等
|
|
18
|
+
|
|
19
|
+
// 读取插件配置文件
|
|
20
|
+
const configPath = Addon.getDir(addonName, 'addon.config.json');
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const configContent = readFileSync(configPath, 'utf-8');
|
|
24
|
+
const config = JSON.parse(configContent);
|
|
25
|
+
|
|
26
|
+
addonList.push({
|
|
27
|
+
name: config.name || addonName,
|
|
28
|
+
title: config.title || addonName,
|
|
29
|
+
version: config.version || '1.0.0',
|
|
30
|
+
description: config.description || '',
|
|
31
|
+
enabled: true
|
|
32
|
+
});
|
|
33
|
+
} catch (error) {
|
|
34
|
+
// 配置文件不存在或解析失败,使用默认值
|
|
35
|
+
addonList.push({
|
|
36
|
+
name: addonName,
|
|
37
|
+
title: addonName,
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
description: '',
|
|
40
|
+
enabled: true
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return Yes('获取成功', addonList);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取更新日志
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes } from 'befly';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: '获取更新日志',
|
|
9
|
+
handler: async (befly, ctx) => {
|
|
10
|
+
// 更新日志数据(实际项目中可以从配置文件或数据库读取)
|
|
11
|
+
const changelog = [
|
|
12
|
+
{
|
|
13
|
+
version: 'v1.0.0',
|
|
14
|
+
date: '2025-10-25',
|
|
15
|
+
changes: ['新增角色权限管理功能', '新增菜单权限分配功能', '新增接口权限管理功能', '优化菜单同步性能', '优化字段类型验证', '修复数组类型字段验证bug']
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
version: 'v0.9.0',
|
|
19
|
+
date: '2025-10-20',
|
|
20
|
+
changes: ['初始版本发布', '完成基础框架搭建', '实现用户认证功能', '实现RBAC权限系统']
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
version: 'v0.8.0',
|
|
24
|
+
date: '2025-10-15',
|
|
25
|
+
changes: ['完成数据库设计', '实现核心API', '添加字段验证器', '集成Redis缓存']
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// 根据 limit 参数返回指定数量的日志
|
|
30
|
+
const limit = ctx.body.limit || 5;
|
|
31
|
+
const lists = changelog.slice(0, limit);
|
|
32
|
+
|
|
33
|
+
return Yes('获取成功', {
|
|
34
|
+
lists: lists
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取系统配置状态
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Yes } from 'befly';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: '获取配置状态',
|
|
9
|
+
handler: async (befly, ctx) => {
|
|
10
|
+
const status = {
|
|
11
|
+
database: { status: 'ok', latency: 0 },
|
|
12
|
+
redis: { status: 'ok', latency: 0 },
|
|
13
|
+
fileSystem: { status: 'ok' },
|
|
14
|
+
email: { status: 'warning', message: '未配置' },
|
|
15
|
+
oss: { status: 'warning', message: '未配置' }
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// 检查数据库连接
|
|
19
|
+
try {
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
await befly.db.query('SELECT 1');
|
|
22
|
+
status.database.latency = Date.now() - startTime;
|
|
23
|
+
status.database.status = 'ok';
|
|
24
|
+
} catch (error) {
|
|
25
|
+
status.database.status = 'error';
|
|
26
|
+
status.database.message = '连接失败';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 检查 Redis 连接
|
|
30
|
+
try {
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
await befly.redis.ping();
|
|
33
|
+
status.redis.latency = Date.now() - startTime;
|
|
34
|
+
status.redis.status = 'ok';
|
|
35
|
+
} catch (error) {
|
|
36
|
+
status.redis.status = 'error';
|
|
37
|
+
status.redis.message = '连接失败';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 检查邮件配置
|
|
41
|
+
if (process.env.SMTP_HOST && process.env.SMTP_PORT) {
|
|
42
|
+
status.email.status = 'ok';
|
|
43
|
+
status.email.message = '已配置';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 检查 OSS 配置
|
|
47
|
+
if (process.env.OSS_ACCESS_KEY && process.env.OSS_SECRET_KEY) {
|
|
48
|
+
status.oss.status = 'ok';
|
|
49
|
+
status.oss.message = '已配置';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return Yes('获取成功', status);
|
|
53
|
+
}
|
|
54
|
+
};
|