befly 3.75.3 → 3.76.1
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/insert.js +2 -1
- package/apis/admin/update.js +2 -3
- package/checks/corns.js +54 -0
- package/hooks/permission.js +4 -2
- package/index.js +16 -1
- package/package.json +1 -1
- package/paths.js +14 -0
- package/plugins/cron.js +31 -10
- package/sync/syncUtil.js +7 -13
- package/tables/api.json +2 -2
- package/utils/is.js +14 -0
- package/utils/scanSources.js +16 -2
- package/apis/role/delete.js +0 -41
- package/apis/role/insert.js +0 -45
- package/apis/role/save.js +0 -36
- package/apis/role/update.js +0 -66
package/apis/admin/insert.js
CHANGED
package/apis/admin/update.js
CHANGED
|
@@ -16,8 +16,7 @@ export default {
|
|
|
16
16
|
lastLoginIp: adminTable.lastLoginIp,
|
|
17
17
|
lastLoginTime: adminTable.lastLoginTime,
|
|
18
18
|
password: adminTable.password,
|
|
19
|
-
phone: adminTable.phone
|
|
20
|
-
roleType: adminTable.roleType
|
|
19
|
+
phone: adminTable.phone
|
|
21
20
|
},
|
|
22
21
|
required: ["id"],
|
|
23
22
|
handler: async (befly, ctx) => {
|
|
@@ -79,7 +78,7 @@ export default {
|
|
|
79
78
|
updateData.password = await hashPassword(ctx.body.password);
|
|
80
79
|
}
|
|
81
80
|
if (ctx.body.phone !== undefined) updateData.phone = ctx.body.phone;
|
|
82
|
-
|
|
81
|
+
// 管理员表类型固定为 admin,roleType 不允许通过更新接口修改
|
|
83
82
|
if (ctx.body.username !== undefined) updateData.username = ctx.body.username;
|
|
84
83
|
if (ctx.body.nickname !== undefined) updateData.nickname = ctx.body.nickname;
|
|
85
84
|
if (ctx.body.roleCode !== undefined) updateData.roleCode = ctx.body.roleCode;
|
package/checks/corns.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Logger } from "../lib/logger.js";
|
|
2
|
+
import { formatValidationIssues } from "../utils/formatValidationIssues.js";
|
|
3
|
+
import { isFunction, isPlainObject, isValidCronSchedule } from "../utils/is.js";
|
|
4
|
+
|
|
5
|
+
// scanFiles 基础字段 + 定时器业务字段(schedule/handler;时区统一走 config.tz)
|
|
6
|
+
const cornKeys = new Set(["source", "type", "filePath", "relativePath", "fileName", "apiPath", "schedule", "handler"]);
|
|
7
|
+
|
|
8
|
+
export const checkCorn = (corns) => {
|
|
9
|
+
if (!Array.isArray(corns)) {
|
|
10
|
+
const errors = formatValidationIssues([{ path: [], message: "必须是数组" }], { item: corns, itemLabel: "corn" });
|
|
11
|
+
Logger.warn(`定时器校验失败`, { errors: errors });
|
|
12
|
+
return errors;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const errors = [];
|
|
16
|
+
|
|
17
|
+
// 任务名唯一性:文件名即任务名(core + app 合并后全局唯一,防止同名任务被 register 静默去重)
|
|
18
|
+
const nameMap = new Map();
|
|
19
|
+
for (const corn of corns) {
|
|
20
|
+
if (!corn?.fileName) continue;
|
|
21
|
+
nameMap.set(corn.fileName, (nameMap.get(corn.fileName) || 0) + 1);
|
|
22
|
+
}
|
|
23
|
+
const duplicateNames = [...nameMap].filter(([, count]) => count > 1).map(([name]) => name);
|
|
24
|
+
if (duplicateNames.length > 0) {
|
|
25
|
+
const duplicateCorns = corns.filter((corn) => duplicateNames.includes(corn.fileName));
|
|
26
|
+
errors.push(
|
|
27
|
+
...formatValidationIssues(
|
|
28
|
+
duplicateCorns.map((corn, index) => ({ path: [index], message: `任务名 ${corn.fileName} 重复,请显式区分` })),
|
|
29
|
+
{ items: duplicateCorns, itemLabel: "corn" }
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const [index, corn] of corns.entries()) {
|
|
35
|
+
if (!isPlainObject(corn)) {
|
|
36
|
+
errors.push(...formatValidationIssues([{ path: [index], message: "必须是对象格式" }], { items: corns, itemLabel: "corn" }));
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const issues = [];
|
|
41
|
+
for (const key of Object.keys(corn)) {
|
|
42
|
+
if (!cornKeys.has(key)) issues.push({ path: [key], message: "不允许出现" });
|
|
43
|
+
}
|
|
44
|
+
if (!isFunction(corn.handler)) issues.push({ path: ["handler"], message: "必须是函数" });
|
|
45
|
+
if (!isValidCronSchedule(corn.schedule)) issues.push({ path: ["schedule"], message: "必须是可注册的 cron 表达式" });
|
|
46
|
+
if (issues.length === 0) continue;
|
|
47
|
+
errors.push(...formatValidationIssues(issues, { items: corns, itemLabel: "corn" }));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (errors.length > 0) {
|
|
51
|
+
Logger.warn(`定时器校验失败`, { errors: errors });
|
|
52
|
+
}
|
|
53
|
+
return errors;
|
|
54
|
+
};
|
package/hooks/permission.js
CHANGED
|
@@ -6,9 +6,11 @@ import { ErrorResponse } from "../utils/response.js";
|
|
|
6
6
|
/**
|
|
7
7
|
* 权限检查钩子
|
|
8
8
|
* - 接口无需权限(auth=false):直接通过
|
|
9
|
-
* - auth
|
|
9
|
+
* - auth 为角色类型白名单(string[],如 ["admin"] / ["user"]):按 ctx.roleType 校验。
|
|
10
|
+
* 类型与角色解耦:管理员表(beflyAdmin)roleType 固定 "admin",用户表固定 "user",
|
|
11
|
+
* 任意角色 code 的管理员共享 "admin" 类型门槛,细粒度授权由下方集合检查(roleCode)负责
|
|
10
12
|
* - 用户未登录:返回 401
|
|
11
|
-
* - 开发者角色(dev
|
|
13
|
+
* - 开发者角色(dev):最高权限,直接通过(唯一内置角色,不可创建)
|
|
12
14
|
* - 其他角色:检查 Redis 中的角色权限集合
|
|
13
15
|
*/
|
|
14
16
|
export default {
|
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// 检查
|
|
7
7
|
import { checkApi } from "#befly/checks/api.js";
|
|
8
8
|
import { checkConfig } from "#befly/checks/config.js";
|
|
9
|
+
import { checkCorn } from "#befly/checks/corns.js";
|
|
9
10
|
import { checkHook, checkPlugin } from "#befly/checks/items.js";
|
|
10
11
|
import { checkMenu } from "#befly/checks/menu.js";
|
|
11
12
|
import { checkTable } from "#befly/checks/table.js";
|
|
@@ -18,6 +19,8 @@ import { BEFLY_ADMIN_TABLE, BEFLY_API_TABLE, BEFLY_MENU_TABLE, BEFLY_ROLE_TABLE
|
|
|
18
19
|
import { Connect } from "#befly/lib/connect.js";
|
|
19
20
|
// oxlint-disable-next-line unicorn/prefer-export-from -- Logger 同时被本文件大量使用,不能合并为 re-export
|
|
20
21
|
import { Logger } from "#befly/lib/logger.js";
|
|
22
|
+
// 定时任务
|
|
23
|
+
import { registerCorns } from "#befly/plugins/cron.js";
|
|
21
24
|
// 路由处理
|
|
22
25
|
import { apiHandler } from "#befly/router/api.js";
|
|
23
26
|
import { staticHandler } from "#befly/router/static.js";
|
|
@@ -108,7 +111,7 @@ export async function createBefly(config = {}, menus = []) {
|
|
|
108
111
|
Logger.info(`启动 检查配置 耗时 ${calcPerfTime(stageStartTime)}`);
|
|
109
112
|
|
|
110
113
|
stageStartTime = Bun.nanoseconds();
|
|
111
|
-
const { apis, tables, plugins, hooks } = await scanSources();
|
|
114
|
+
const { apis, tables, plugins, hooks, corns } = await scanSources();
|
|
112
115
|
Logger.info(`启动 扫描源码 耗时 ${calcPerfTime(stageStartTime)}`);
|
|
113
116
|
|
|
114
117
|
stageStartTime = Bun.nanoseconds();
|
|
@@ -127,6 +130,10 @@ export async function createBefly(config = {}, menus = []) {
|
|
|
127
130
|
const hookErrors = await checkHook(hooks);
|
|
128
131
|
Logger.info(`启动 检查钩子 耗时 ${calcPerfTime(stageStartTime)}`);
|
|
129
132
|
|
|
133
|
+
stageStartTime = Bun.nanoseconds();
|
|
134
|
+
const cornErrors = await checkCorn(corns);
|
|
135
|
+
Logger.info(`启动 检查定时器 耗时 ${calcPerfTime(stageStartTime)}`);
|
|
136
|
+
|
|
130
137
|
stageStartTime = Bun.nanoseconds();
|
|
131
138
|
const menuErrors = await checkMenu(mergedMenus);
|
|
132
139
|
Logger.info(`启动 检查菜单 耗时 ${calcPerfTime(stageStartTime)}`);
|
|
@@ -138,6 +145,7 @@ export async function createBefly(config = {}, menus = []) {
|
|
|
138
145
|
{ check: "表结构", errors: tableErrors },
|
|
139
146
|
{ check: "插件", errors: pluginErrors },
|
|
140
147
|
{ check: "钩子", errors: hookErrors },
|
|
148
|
+
{ check: "定时器", errors: cornErrors },
|
|
141
149
|
{ check: "菜单", errors: menuErrors }
|
|
142
150
|
].filter((entry) => entry.errors.length > 0);
|
|
143
151
|
|
|
@@ -161,6 +169,7 @@ export async function createBefly(config = {}, menus = []) {
|
|
|
161
169
|
apis: apis,
|
|
162
170
|
hooks: hooks,
|
|
163
171
|
plugins: plugins,
|
|
172
|
+
corns: corns,
|
|
164
173
|
createdByFactory: true
|
|
165
174
|
});
|
|
166
175
|
}
|
|
@@ -179,6 +188,7 @@ export class Befly {
|
|
|
179
188
|
this.hooks = Array.isArray(init.hooks) ? init.hooks : [];
|
|
180
189
|
this.apis = Array.isArray(init.apis) ? init.apis : [];
|
|
181
190
|
this.plugins = Array.isArray(init.plugins) ? init.plugins : [];
|
|
191
|
+
this.corns = Array.isArray(init.corns) ? init.corns : [];
|
|
182
192
|
this.createdByFactory = init.createdByFactory === true;
|
|
183
193
|
}
|
|
184
194
|
|
|
@@ -217,6 +227,11 @@ export class Befly {
|
|
|
217
227
|
Logger.info(`启动 插件 ${item.fileName} 耗时 ${calcPerfTime(pluginStartTime)}`);
|
|
218
228
|
}
|
|
219
229
|
|
|
230
|
+
// 注册定时器(依赖插件加载完成的 ctx.cron,非主进程自动跳过)
|
|
231
|
+
stageStartTime = Bun.nanoseconds();
|
|
232
|
+
registerCorns(this.context, this.corns);
|
|
233
|
+
Logger.info(`启动 注册定时器 耗时 ${calcPerfTime(stageStartTime)}`);
|
|
234
|
+
|
|
220
235
|
stageStartTime = Bun.nanoseconds();
|
|
221
236
|
await syncReady(this.context);
|
|
222
237
|
Logger.info(`启动 同步就绪 耗时 ${calcPerfTime(stageStartTime)}`);
|
package/package.json
CHANGED
package/paths.js
CHANGED
|
@@ -46,6 +46,13 @@ export const coreCheckDir = join(moduleDir, "checks");
|
|
|
46
46
|
*/
|
|
47
47
|
export const corePluginDir = join(moduleDir, "plugins");
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Core 框架定时器目录
|
|
51
|
+
* @description packages/core/corns/
|
|
52
|
+
* @usage 存放框架内置定时任务(每文件一个定时器)
|
|
53
|
+
*/
|
|
54
|
+
export const coreCornDir = join(moduleDir, "corns");
|
|
55
|
+
|
|
49
56
|
/**
|
|
50
57
|
* Core 框架钩子目录
|
|
51
58
|
* @description packages/core/hooks/
|
|
@@ -90,6 +97,13 @@ export const appCheckDir = join(appDir, "checks");
|
|
|
90
97
|
*/
|
|
91
98
|
export const appPluginDir = join(appDir, "plugins");
|
|
92
99
|
|
|
100
|
+
/**
|
|
101
|
+
* 项目定时器目录
|
|
102
|
+
* @description {appDir}/corns/
|
|
103
|
+
* @usage 存放用户业务定时任务(每文件一个定时器)
|
|
104
|
+
*/
|
|
105
|
+
export const appCornDir = join(appDir, "corns");
|
|
106
|
+
|
|
93
107
|
/**
|
|
94
108
|
* 项目钩子目录
|
|
95
109
|
* @description {appDir}/hooks/
|
package/plugins/cron.js
CHANGED
|
@@ -4,7 +4,8 @@ import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
|
4
4
|
* 基于 Bun.cron 提供进程内定时任务能力。
|
|
5
5
|
*/
|
|
6
6
|
import { Logger } from "../lib/logger.js";
|
|
7
|
-
import {
|
|
7
|
+
import { createError } from "../utils/error.js";
|
|
8
|
+
import { isPrimaryProcess, isValidCronSchedule } from "../utils/is.js";
|
|
8
9
|
|
|
9
10
|
const jobs = new Map();
|
|
10
11
|
|
|
@@ -24,15 +25,26 @@ function register(context, name, schedule, task) {
|
|
|
24
25
|
return jobs.get(name);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
// 前置校验:表达式非法时启动即报错,避免 Bun.cron 注册时抛裸错
|
|
29
|
+
if (!isValidCronSchedule(schedule)) {
|
|
30
|
+
throw createError(`定时任务 ${name} 的 schedule 不是合法的 cron 表达式`, { code: "validation", subsystem: "cron", operation: "register" });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 时区统一走 config.tz(checkConfig 已校验 IANA 合法性),未配置时使用系统时区
|
|
34
|
+
const tz = context.config?.tz;
|
|
35
|
+
const job = Bun.cron(
|
|
36
|
+
schedule,
|
|
37
|
+
async function () {
|
|
38
|
+
Logger.info(`[定时任务] ${name} 开始执行`);
|
|
39
|
+
try {
|
|
40
|
+
await task(context);
|
|
41
|
+
Logger.info(`[定时任务] ${name} 执行完成`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
Logger.error(`[定时任务] ${name} 执行失败`, error);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
tz ? { tz: tz } : undefined
|
|
47
|
+
);
|
|
36
48
|
|
|
37
49
|
jobs.set(name, job);
|
|
38
50
|
return job;
|
|
@@ -73,6 +85,15 @@ function second(context, name, seconds, task) {
|
|
|
73
85
|
return job;
|
|
74
86
|
}
|
|
75
87
|
|
|
88
|
+
/**
|
|
89
|
+
* 批量注册扫描到的定时器(corns/*.js),任务名取文件名
|
|
90
|
+
*/
|
|
91
|
+
export function registerCorns(context, corns) {
|
|
92
|
+
for (const corn of corns) {
|
|
93
|
+
register(context, corn.fileName, corn.schedule, corn.handler);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
76
97
|
/**
|
|
77
98
|
* 停止所有已注册的定时任务
|
|
78
99
|
*/
|
package/sync/syncUtil.js
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
// 接口 auth 的存储协议(serializeAuth 与 authAllowsRole 配套使用):
|
|
2
|
-
// - true ->
|
|
3
|
-
// - false ->
|
|
4
|
-
// - string[] -> "a,b,c"
|
|
5
|
-
export const AUTH_YES = "是";
|
|
6
|
-
export const AUTH_NO = "否";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 序列化 api.auth 为存储字符串
|
|
10
|
-
*/
|
|
2
|
+
// - true -> 1(需登录)
|
|
3
|
+
// - false -> 0(免登录)
|
|
4
|
+
// - string[] -> "a,b,c"(角色类型白名单,如 admin/user)
|
|
11
5
|
export function serializeAuth(auth) {
|
|
12
|
-
return auth === false ?
|
|
6
|
+
return auth === false ? 0 : Array.isArray(auth) ? auth.join(",") : 1;
|
|
13
7
|
}
|
|
14
8
|
|
|
15
9
|
/**
|
|
16
|
-
* 判断 auth
|
|
10
|
+
* 判断 auth 存储值是否授权给指定角色
|
|
17
11
|
*/
|
|
18
12
|
export function authAllowsRole(auth, roleCode) {
|
|
19
|
-
if (auth ===
|
|
20
|
-
if (typeof auth === "string" && auth !==
|
|
13
|
+
if (Number(auth) === 0) return true;
|
|
14
|
+
if (typeof auth === "string" && Number(auth) !== 1) {
|
|
21
15
|
return auth.split(",").some((code) => code.trim() === roleCode);
|
|
22
16
|
}
|
|
23
17
|
return false;
|
package/tables/api.json
CHANGED
package/utils/is.js
CHANGED
|
@@ -187,6 +187,20 @@ export function isValidTimeZone(value) {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
/**
|
|
191
|
+
* 判断值是否为可注册的 cron 表达式(与 Bun.cron 注册行为一致:
|
|
192
|
+
* parse 抛异常或 8 年内无匹配都视为非法,避免启动后才报错)。
|
|
193
|
+
*/
|
|
194
|
+
export function isValidCronSchedule(value) {
|
|
195
|
+
if (!isString(value)) return false;
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
return Bun.cron.parse(value) !== null;
|
|
199
|
+
} catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
190
204
|
/**
|
|
191
205
|
* 判断值是否为有限整数。
|
|
192
206
|
*/
|
package/utils/scanSources.js
CHANGED
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
coreHookDir,
|
|
8
8
|
appHookDir,
|
|
9
9
|
coreApiDir,
|
|
10
|
-
appApiDir
|
|
10
|
+
appApiDir,
|
|
11
|
+
coreCornDir,
|
|
12
|
+
appCornDir
|
|
11
13
|
} from "../paths.js";
|
|
12
14
|
import { createError } from "./error.js";
|
|
13
15
|
import { scanFiles } from "./scanFiles.js";
|
|
@@ -17,6 +19,7 @@ export const scanSources = async () => {
|
|
|
17
19
|
const plugins = [];
|
|
18
20
|
const hooks = [];
|
|
19
21
|
const tables = {};
|
|
22
|
+
const corns = [];
|
|
20
23
|
|
|
21
24
|
// 处理表格
|
|
22
25
|
const allCoreTables = await scanFiles(coreTableDir, "core", "table", "*.json");
|
|
@@ -48,6 +51,16 @@ export const scanSources = async () => {
|
|
|
48
51
|
hooks.push(item);
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
// 处理定时器
|
|
55
|
+
const allCoreCorns = await scanFiles(coreCornDir, "core", "corn", "*.js");
|
|
56
|
+
for (const item of allCoreCorns) {
|
|
57
|
+
corns.push(item);
|
|
58
|
+
}
|
|
59
|
+
const allAppCorns = await scanFiles(appCornDir, "app", "corn", "*.js");
|
|
60
|
+
for (const item of allAppCorns) {
|
|
61
|
+
corns.push(item);
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
// 处理接口
|
|
52
65
|
const allCoreApis = await scanFiles(coreApiDir, "core", "api", "**/*.js");
|
|
53
66
|
for (const item of allCoreApis) {
|
|
@@ -70,6 +83,7 @@ export const scanSources = async () => {
|
|
|
70
83
|
hooks: hooks,
|
|
71
84
|
plugins: plugins,
|
|
72
85
|
apis: apis,
|
|
73
|
-
tables: tables
|
|
86
|
+
tables: tables,
|
|
87
|
+
corns: corns
|
|
74
88
|
};
|
|
75
89
|
};
|
package/apis/role/delete.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { getRoleById, systemRoleCodes } from "./_role.js";
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
name: "删除角色",
|
|
5
|
-
method: "POST",
|
|
6
|
-
body: "none",
|
|
7
|
-
auth: true,
|
|
8
|
-
fields: {
|
|
9
|
-
id: { name: "ID", input: "integer", min: 1, max: null }
|
|
10
|
-
},
|
|
11
|
-
required: ["id"],
|
|
12
|
-
handler: async (befly, ctx) => {
|
|
13
|
-
const role = await getRoleById(befly, ctx.body.id, ["code"]);
|
|
14
|
-
|
|
15
|
-
if (!role.data?.code) {
|
|
16
|
-
return befly.tool.No("角色不存在");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (systemRoleCodes.includes(role.data.code)) {
|
|
20
|
-
return befly.tool.No(`系统角色 [${role.data.code}] 不允许删除`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const adminList = await befly.mysql.getList({
|
|
24
|
-
table: "beflyAdmin",
|
|
25
|
-
where: { roleCode: role.data.code }
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (adminList.data.total > 0) {
|
|
29
|
-
return befly.tool.No("该角色已分配给用户,无法删除");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
await befly.mysql.delData({
|
|
33
|
-
table: "beflyRole",
|
|
34
|
-
where: { id: ctx.body.id }
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
await befly.cache.deleteRoleApis(role.data.code);
|
|
38
|
-
|
|
39
|
-
return befly.tool.Yes("操作成功");
|
|
40
|
-
}
|
|
41
|
-
};
|
package/apis/role/insert.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import roleTable from "#befly/tables/role.json";
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
name: "创建角色",
|
|
5
|
-
method: "POST",
|
|
6
|
-
body: "none",
|
|
7
|
-
auth: true,
|
|
8
|
-
fields: {
|
|
9
|
-
name: roleTable.name,
|
|
10
|
-
code: roleTable.code,
|
|
11
|
-
description: roleTable.description,
|
|
12
|
-
menus: roleTable.menus,
|
|
13
|
-
apis: roleTable.apis,
|
|
14
|
-
sort: roleTable.sort
|
|
15
|
-
},
|
|
16
|
-
required: [],
|
|
17
|
-
handler: async (befly, ctx) => {
|
|
18
|
-
const menuPaths = ctx.body.menus || [];
|
|
19
|
-
const apiPaths = ctx.body.apis || [];
|
|
20
|
-
const existing = await befly.mysql.getOne({
|
|
21
|
-
table: "beflyRole",
|
|
22
|
-
where: { code: ctx.body.code }
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
if (existing.data?.id) {
|
|
26
|
-
return befly.tool.No("角色代码已存在");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const roleId = await befly.mysql.insData({
|
|
30
|
-
table: "beflyRole",
|
|
31
|
-
data: {
|
|
32
|
-
name: ctx.body.name,
|
|
33
|
-
code: ctx.body.code,
|
|
34
|
-
description: ctx.body.description,
|
|
35
|
-
menus: menuPaths,
|
|
36
|
-
apis: apiPaths,
|
|
37
|
-
sort: ctx.body.sort
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
await befly.cache.refreshRoleApis(ctx.body.code, apiPaths);
|
|
42
|
-
|
|
43
|
-
return befly.tool.Yes("操作成功", { id: roleId.data });
|
|
44
|
-
}
|
|
45
|
-
};
|
package/apis/role/save.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import adminTable from "#befly/tables/admin.json";
|
|
2
|
-
import roleTable from "#befly/tables/role.json";
|
|
3
|
-
|
|
4
|
-
import { getRoleByCode } from "./_role.js";
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
name: "角色保存",
|
|
8
|
-
method: "POST",
|
|
9
|
-
body: "none",
|
|
10
|
-
auth: true,
|
|
11
|
-
fields: {
|
|
12
|
-
adminId: adminTable.id,
|
|
13
|
-
roleCode: roleTable.code
|
|
14
|
-
},
|
|
15
|
-
required: ["adminId", "roleCode"],
|
|
16
|
-
handler: async (befly, ctx) => {
|
|
17
|
-
const role = await getRoleByCode(befly, ctx.body.roleCode);
|
|
18
|
-
|
|
19
|
-
if (!role.data?.id) {
|
|
20
|
-
return befly.tool.No("角色不存在");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const roleType = role.data.code === "dev" || role.data.code === "admin" ? "admin" : "user";
|
|
24
|
-
|
|
25
|
-
await befly.mysql.updData({
|
|
26
|
-
table: "beflyAdmin",
|
|
27
|
-
where: { id: ctx.body.adminId },
|
|
28
|
-
data: {
|
|
29
|
-
roleCode: role.data.code,
|
|
30
|
-
roleType: roleType
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return befly.tool.Yes("操作成功");
|
|
35
|
-
}
|
|
36
|
-
};
|
package/apis/role/update.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import roleTable from "#befly/tables/role.json";
|
|
2
|
-
|
|
3
|
-
import { getRoleById } from "./_role.js";
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
name: "更新角色",
|
|
7
|
-
method: "POST",
|
|
8
|
-
body: "none",
|
|
9
|
-
auth: true,
|
|
10
|
-
fields: {
|
|
11
|
-
id: roleTable.id,
|
|
12
|
-
name: roleTable.name,
|
|
13
|
-
code: roleTable.code,
|
|
14
|
-
description: roleTable.description,
|
|
15
|
-
menus: roleTable.menus,
|
|
16
|
-
apis: roleTable.apis,
|
|
17
|
-
sort: roleTable.sort
|
|
18
|
-
},
|
|
19
|
-
required: ["id"],
|
|
20
|
-
handler: async (befly, ctx) => {
|
|
21
|
-
const role = await getRoleById(befly, ctx.body.id);
|
|
22
|
-
|
|
23
|
-
if (!role.data?.id) {
|
|
24
|
-
return befly.tool.No("角色不存在");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (ctx.body.code !== undefined) {
|
|
28
|
-
const existing = await befly.mysql.getOne({
|
|
29
|
-
table: "beflyRole",
|
|
30
|
-
where: {
|
|
31
|
-
code: ctx.body.code,
|
|
32
|
-
id$not: ctx.body.id
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
if (existing.data?.id) {
|
|
37
|
-
return befly.tool.No("角色代码已被其他角色使用");
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const roleCode = ctx.body.code === undefined ? role.data.code : ctx.body.code;
|
|
42
|
-
const apiPaths = ctx.body.apis === undefined ? role.data.apis || [] : ctx.body.apis;
|
|
43
|
-
const menuPaths = ctx.body.menus === undefined ? role.data.menus || [] : ctx.body.menus;
|
|
44
|
-
|
|
45
|
-
await befly.mysql.updData({
|
|
46
|
-
table: "beflyRole",
|
|
47
|
-
where: { id: ctx.body.id },
|
|
48
|
-
data: {
|
|
49
|
-
name: ctx.body.name,
|
|
50
|
-
code: roleCode,
|
|
51
|
-
description: ctx.body.description,
|
|
52
|
-
menus: menuPaths,
|
|
53
|
-
apis: apiPaths,
|
|
54
|
-
sort: ctx.body.sort
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
if (role.data.code !== roleCode) {
|
|
59
|
-
await befly.cache.deleteRoleApis(role.data.code);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
await befly.cache.refreshRoleApis(roleCode, apiPaths);
|
|
63
|
-
|
|
64
|
-
return befly.tool.Yes("操作成功");
|
|
65
|
-
}
|
|
66
|
-
};
|