befly 3.24.18 → 3.24.20
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/_apis.js +20 -0
- package/apis/admin/delete.js +3 -1
- package/apis/admin/detail.js +1 -3
- package/apis/admin/select.js +9 -7
- package/apis/admin/update.js +2 -2
- package/apis/api/all.js +6 -11
- package/apis/api/select.js +18 -19
- package/apis/dashboard/environmentInfo.js +6 -1
- package/apis/dashboard/serviceStatus.js +78 -60
- package/apis/dict/_dict.js +24 -0
- package/apis/dict/all.js +6 -4
- package/apis/dict/detail.js +9 -5
- package/apis/dict/insert.js +4 -11
- package/apis/dict/items.js +5 -6
- package/apis/dict/select.js +13 -9
- package/apis/dict/update.js +9 -13
- package/apis/dictType/select.js +4 -4
- package/apis/email/config.js +9 -11
- package/apis/email/logList.js +14 -4
- package/apis/email/send.js +10 -3
- package/apis/email/verify.js +9 -7
- package/apis/loginLog/select.js +11 -4
- package/apis/menu/all.js +13 -19
- package/apis/menu/select.js +19 -14
- package/apis/operateLog/select.js +13 -4
- package/apis/role/_role.js +21 -0
- package/apis/role/all.js +1 -3
- package/apis/role/apiSave.js +8 -15
- package/apis/role/apis.js +4 -10
- package/apis/role/delete.js +28 -36
- package/apis/role/detail.js +4 -10
- package/apis/role/insert.js +12 -10
- package/apis/role/menuSave.js +9 -15
- package/apis/role/menus.js +4 -10
- package/apis/role/save.js +19 -23
- package/apis/role/select.js +12 -9
- package/apis/role/update.js +14 -15
- package/apis/source/imageList.js +3 -3
- package/apis/sysConfig/get.js +11 -23
- package/apis/sysConfig/insert.js +22 -27
- package/apis/sysConfig/select.js +11 -5
- package/apis/sysConfig/update.js +33 -38
- package/apis/tongJi/_tongJi.js +41 -0
- package/apis/tongJi/cacheHealth.js +192 -0
- package/apis/tongJi/errorList.js +26 -27
- package/apis/tongJi/errorReport.js +72 -18
- package/apis/tongJi/errorStats.js +154 -30
- package/apis/tongJi/fallbackReset.js +61 -0
- package/apis/tongJi/infoReport.js +112 -24
- package/apis/tongJi/infoStats.js +164 -84
- package/apis/tongJi/onlineReport.js +58 -73
- package/apis/tongJi/onlineStats.js +140 -151
- package/checks/config.js +44 -1
- package/configs/beflyConfig.json +10 -1
- package/hooks/permission.js +6 -2
- package/hooks/rateLimit.js +245 -0
- package/index.js +25 -0
- package/lib/cacheHelper.js +105 -60
- package/lib/dbHelper.js +1 -1
- package/lib/dbParse.js +61 -99
- package/lib/dbUtil.js +101 -21
- package/lib/redisHelper.js +93 -0
- package/lib/sqlBuilder.js +6 -0
- package/package.json +2 -2
- package/plugins/email.js +4 -3
- package/utils/email.js +3 -0
- package/apis/admin/cacheRefresh.js +0 -122
- package/apis/dashboard/configStatus.js +0 -39
- package/apis/dashboard/performanceMetrics.js +0 -20
- package/apis/dashboard/permissionStats.js +0 -27
- package/apis/dashboard/systemInfo.js +0 -19
|
@@ -1,41 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getDateYmdNumber } from "#root/utils/datetime.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function toOnlineStatsNumber(value) {
|
|
6
|
-
const num = Number(value);
|
|
7
|
-
|
|
8
|
-
if (!Number.isFinite(num)) {
|
|
9
|
-
return 0;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return num;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function getOnlineStatsWeekStartDate(timestamp = Date.now()) {
|
|
16
|
-
const date = Reflect.construct(Date, [timestamp]);
|
|
17
|
-
const day = date.getDay();
|
|
18
|
-
const offset = day === 0 ? -6 : 1 - day;
|
|
19
|
-
|
|
20
|
-
return getDateYmdNumber(addDays(timestamp, offset));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function getOnlineStatsMonthStartDate(timestamp = Date.now()) {
|
|
24
|
-
const date = Reflect.construct(Date, [timestamp]);
|
|
25
|
-
|
|
26
|
-
return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function getOnlineStatsRecentDateList(now = Date.now(), limit = ONLINE_STATS_DAY_LIMIT) {
|
|
30
|
-
const list = [];
|
|
3
|
+
import { getTongJiMonthStartDate, getTongJiNumber, getTongJiRecentDateList, getTongJiWeekStartDate } from "./_tongJi.js";
|
|
31
4
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return list;
|
|
37
|
-
}
|
|
5
|
+
const ONLINE_STATS_DAY_LIMIT = 30;
|
|
6
|
+
const ONLINE_STATS_ACTIVE_KEY = "online:active";
|
|
38
7
|
|
|
8
|
+
// 构造全量统计的 Redis 键。
|
|
39
9
|
function getPeriodKeys(periodType, periodValue) {
|
|
40
10
|
return {
|
|
41
11
|
pv: `online:${periodType}:${periodValue}:pv`,
|
|
@@ -44,12 +14,9 @@ function getPeriodKeys(periodType, periodValue) {
|
|
|
44
14
|
};
|
|
45
15
|
}
|
|
46
16
|
|
|
47
|
-
|
|
48
|
-
return encodeURIComponent(String(productName || ""));
|
|
49
|
-
}
|
|
50
|
-
|
|
17
|
+
// 构造单产品统计的 Redis 键。
|
|
51
18
|
function getOnlineStatsProductPeriodKeys(periodType, periodValue, productName) {
|
|
52
|
-
const productKey =
|
|
19
|
+
const productKey = encodeURIComponent(productName);
|
|
53
20
|
|
|
54
21
|
return {
|
|
55
22
|
pv: `online:${periodType}:${periodValue}:product:${productKey}:pv`,
|
|
@@ -58,83 +25,153 @@ function getOnlineStatsProductPeriodKeys(periodType, periodValue, productName) {
|
|
|
58
25
|
};
|
|
59
26
|
}
|
|
60
27
|
|
|
61
|
-
|
|
62
|
-
|
|
28
|
+
// 构造单产品实时在线集合的 Redis 键。
|
|
29
|
+
function getOnlineStatsActiveProductKey(productName) {
|
|
30
|
+
const productKey = encodeURIComponent(productName);
|
|
31
|
+
|
|
32
|
+
return `online:active:product:${productKey}`;
|
|
63
33
|
}
|
|
64
34
|
|
|
35
|
+
// 读取 Redis 字符串并转成统计数字。
|
|
65
36
|
async function getRedisNumber(befly, key) {
|
|
66
|
-
return
|
|
37
|
+
return getTongJiNumber(await befly.redis.getString(key));
|
|
67
38
|
}
|
|
68
39
|
|
|
40
|
+
// 读取某个周期的全量统计数据。
|
|
69
41
|
async function getOnlineStatsPeriodData(befly, periodType, periodValue) {
|
|
70
42
|
const keys = getPeriodKeys(periodType, periodValue);
|
|
71
43
|
return {
|
|
72
44
|
reportTime: await getRedisNumber(befly, keys.reportTime),
|
|
73
45
|
pv: await getRedisNumber(befly, keys.pv),
|
|
74
|
-
uv:
|
|
46
|
+
uv: getTongJiNumber(await befly.redis.scard(keys.members))
|
|
75
47
|
};
|
|
76
48
|
}
|
|
77
49
|
|
|
50
|
+
// 读取某个周期的单产品统计数据。
|
|
78
51
|
async function getOnlineStatsProductPeriodData(befly, periodType, periodValue, productName) {
|
|
79
52
|
const keys = getOnlineStatsProductPeriodKeys(periodType, periodValue, productName);
|
|
80
53
|
|
|
81
54
|
return {
|
|
82
55
|
reportTime: await getRedisNumber(befly, keys.reportTime),
|
|
83
56
|
pv: await getRedisNumber(befly, keys.pv),
|
|
84
|
-
uv:
|
|
57
|
+
uv: getTongJiNumber(await befly.redis.scard(keys.members))
|
|
85
58
|
};
|
|
86
59
|
}
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
|
|
61
|
+
// 清理过期成员后返回全站实时在线人数。
|
|
62
|
+
async function getOnlineStatsTotalOnlineCount(befly) {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
|
|
65
|
+
await befly.redis.zremrangebyscore(ONLINE_STATS_ACTIVE_KEY, "-inf", now);
|
|
66
|
+
return await befly.redis.zcard(ONLINE_STATS_ACTIVE_KEY);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 清理过期成员后返回单产品实时在线人数。
|
|
70
|
+
async function getOnlineStatsProductOnlineCount(befly, productName) {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
const productKey = getOnlineStatsActiveProductKey(productName);
|
|
73
|
+
|
|
74
|
+
await befly.redis.zremrangebyscore(productKey, "-inf", now);
|
|
75
|
+
return await befly.redis.zcard(productKey);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 构造全量统计的按天趋势列表。
|
|
79
|
+
async function buildOnlineStatsDays(befly, recentDateList) {
|
|
80
|
+
const days = [];
|
|
90
81
|
|
|
91
82
|
for (const item of recentDateList) {
|
|
92
|
-
|
|
93
|
-
if (!productName) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
83
|
+
const dayData = await getOnlineStatsPeriodData(befly, "day", item);
|
|
96
84
|
|
|
97
|
-
|
|
85
|
+
if (dayData.reportTime <= 0) {
|
|
86
|
+
continue;
|
|
98
87
|
}
|
|
88
|
+
|
|
89
|
+
days.push({
|
|
90
|
+
reportDate: item,
|
|
91
|
+
reportTime: dayData.reportTime,
|
|
92
|
+
pv: dayData.pv,
|
|
93
|
+
uv: dayData.uv
|
|
94
|
+
});
|
|
99
95
|
}
|
|
100
96
|
|
|
101
|
-
return
|
|
97
|
+
return days;
|
|
102
98
|
}
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return 0;
|
|
108
|
-
}
|
|
100
|
+
// 构造单产品统计的按天趋势列表。
|
|
101
|
+
async function buildOnlineStatsProductDays(befly, recentDateList, productName) {
|
|
102
|
+
const days = [];
|
|
109
103
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
let onlineCount = 0;
|
|
104
|
+
for (const item of recentDateList) {
|
|
105
|
+
const dayData = await getOnlineStatsProductPeriodData(befly, "day", item, productName);
|
|
113
106
|
|
|
114
|
-
|
|
115
|
-
if (existsList[i]) {
|
|
116
|
-
onlineCount += 1;
|
|
107
|
+
if (dayData.reportTime <= 0) {
|
|
117
108
|
continue;
|
|
118
109
|
}
|
|
119
110
|
|
|
120
|
-
|
|
111
|
+
days.push({
|
|
112
|
+
reportDate: item,
|
|
113
|
+
reportTime: dayData.reportTime,
|
|
114
|
+
pv: dayData.pv,
|
|
115
|
+
uv: dayData.uv
|
|
116
|
+
});
|
|
121
117
|
}
|
|
122
118
|
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
return days;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 按配置项目列表构造项目统计,并按近 30 天 PV 排序。
|
|
123
|
+
async function buildOnlineStatsProducts(befly, recentDateList, reportDate, weekStartDate, monthStartDate, projectLists) {
|
|
124
|
+
const products = [];
|
|
125
|
+
|
|
126
|
+
for (const item of projectLists) {
|
|
127
|
+
const productDays = await buildOnlineStatsProductDays(befly, recentDateList, item.productName);
|
|
128
|
+
let totalPv = 0;
|
|
129
|
+
|
|
130
|
+
for (const productDay of productDays) {
|
|
131
|
+
totalPv += productDay.pv;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const productToday = await getOnlineStatsProductPeriodData(befly, "day", reportDate, item.productName);
|
|
135
|
+
const productWeek = await getOnlineStatsProductPeriodData(befly, "week", weekStartDate, item.productName);
|
|
136
|
+
const productMonth = await getOnlineStatsProductPeriodData(befly, "month", monthStartDate, item.productName);
|
|
137
|
+
const productOnlineCount = await getOnlineStatsProductOnlineCount(befly, item.productName);
|
|
138
|
+
|
|
139
|
+
products.push({
|
|
140
|
+
key: item.productCode,
|
|
141
|
+
productName: item.productName,
|
|
142
|
+
productCode: item.productCode,
|
|
143
|
+
productVersion: item.productVersion || "",
|
|
144
|
+
onlineCount: productOnlineCount,
|
|
145
|
+
today: {
|
|
146
|
+
pv: productToday.pv,
|
|
147
|
+
uv: productToday.uv
|
|
148
|
+
},
|
|
149
|
+
week: {
|
|
150
|
+
pv: productWeek.pv,
|
|
151
|
+
uv: productWeek.uv
|
|
152
|
+
},
|
|
153
|
+
month: {
|
|
154
|
+
pv: productMonth.pv,
|
|
155
|
+
uv: productMonth.uv
|
|
156
|
+
},
|
|
157
|
+
days: productDays,
|
|
158
|
+
totalPv: totalPv
|
|
159
|
+
});
|
|
125
160
|
}
|
|
126
161
|
|
|
127
|
-
|
|
128
|
-
|
|
162
|
+
const sortedProducts = products.toSorted((a, b) => {
|
|
163
|
+
if (b.totalPv !== a.totalPv) {
|
|
164
|
+
return b.totalPv - a.totalPv;
|
|
165
|
+
}
|
|
129
166
|
|
|
130
|
-
|
|
131
|
-
|
|
167
|
+
return String(a.productName).localeCompare(String(b.productName), "zh-CN");
|
|
168
|
+
});
|
|
132
169
|
|
|
133
|
-
for (const item of
|
|
134
|
-
|
|
170
|
+
for (const item of sortedProducts) {
|
|
171
|
+
delete item.totalPv;
|
|
135
172
|
}
|
|
136
173
|
|
|
137
|
-
return
|
|
174
|
+
return sortedProducts;
|
|
138
175
|
}
|
|
139
176
|
|
|
140
177
|
export default {
|
|
@@ -142,88 +179,40 @@ export default {
|
|
|
142
179
|
method: "POST",
|
|
143
180
|
body: "none",
|
|
144
181
|
auth: true,
|
|
145
|
-
fields: {
|
|
182
|
+
fields: {
|
|
183
|
+
productName: { name: "产品名称", input: "string", min: 0, max: 100 }
|
|
184
|
+
},
|
|
146
185
|
required: [],
|
|
147
|
-
|
|
186
|
+
// 汇总当前查询范围的在线统计,并在未筛选产品时附带配置项目统计。
|
|
187
|
+
handler: async (befly, ctx) => {
|
|
148
188
|
const now = Date.now();
|
|
149
189
|
const reportDate = getDateYmdNumber(now);
|
|
150
|
-
const weekStartDate =
|
|
151
|
-
const monthStartDate =
|
|
152
|
-
const recentDateList =
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const weekCurrent = await getOnlineStatsPeriodData(befly, "week", weekStartDate);
|
|
173
|
-
const monthCurrent = await getOnlineStatsPeriodData(befly, "month", monthStartDate);
|
|
174
|
-
const onlineCount = await getOnlineStatsOnlineCount(befly);
|
|
175
|
-
const productNames = await getOnlineStatsProductNames(befly, recentDateList);
|
|
176
|
-
|
|
177
|
-
for (const productName of productNames) {
|
|
178
|
-
const productDays = [];
|
|
179
|
-
|
|
180
|
-
for (const item of recentDateList) {
|
|
181
|
-
const dayData = await getOnlineStatsProductPeriodData(befly, "day", item, productName);
|
|
182
|
-
|
|
183
|
-
if (dayData.reportTime <= 0) {
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
productDays.push({
|
|
188
|
-
reportDate: item,
|
|
189
|
-
reportTime: dayData.reportTime,
|
|
190
|
-
pv: dayData.pv,
|
|
191
|
-
uv: dayData.uv
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const productToday = await getOnlineStatsProductPeriodData(befly, "day", reportDate, productName);
|
|
196
|
-
const productWeek = await getOnlineStatsProductPeriodData(befly, "week", weekStartDate, productName);
|
|
197
|
-
const productMonth = await getOnlineStatsProductPeriodData(befly, "month", monthStartDate, productName);
|
|
198
|
-
|
|
199
|
-
products.push({
|
|
200
|
-
key: productName,
|
|
201
|
-
productName: productName,
|
|
202
|
-
today: {
|
|
203
|
-
pv: productToday.pv,
|
|
204
|
-
uv: productToday.uv
|
|
205
|
-
},
|
|
206
|
-
week: {
|
|
207
|
-
pv: productWeek.pv,
|
|
208
|
-
uv: productWeek.uv
|
|
209
|
-
},
|
|
210
|
-
month: {
|
|
211
|
-
pv: productMonth.pv,
|
|
212
|
-
uv: productMonth.uv
|
|
213
|
-
},
|
|
214
|
-
days: productDays,
|
|
215
|
-
totalPv: sumProductTrendField(productDays, "pv"),
|
|
216
|
-
totalUv: sumProductTrendField(productDays, "uv")
|
|
217
|
-
});
|
|
190
|
+
const weekStartDate = getTongJiWeekStartDate(now);
|
|
191
|
+
const monthStartDate = getTongJiMonthStartDate(now);
|
|
192
|
+
const recentDateList = getTongJiRecentDateList(now, ONLINE_STATS_DAY_LIMIT);
|
|
193
|
+
const productName = String(ctx?.body?.productName || "").trim();
|
|
194
|
+
const hasProductFilter = productName.length > 0;
|
|
195
|
+
const days = hasProductFilter ? await buildOnlineStatsProductDays(befly, recentDateList, productName) : await buildOnlineStatsDays(befly, recentDateList);
|
|
196
|
+
|
|
197
|
+
let currentDay;
|
|
198
|
+
let weekCurrent;
|
|
199
|
+
let monthCurrent;
|
|
200
|
+
let onlineCount;
|
|
201
|
+
|
|
202
|
+
if (hasProductFilter) {
|
|
203
|
+
currentDay = await getOnlineStatsProductPeriodData(befly, "day", reportDate, productName);
|
|
204
|
+
weekCurrent = await getOnlineStatsProductPeriodData(befly, "week", weekStartDate, productName);
|
|
205
|
+
monthCurrent = await getOnlineStatsProductPeriodData(befly, "month", monthStartDate, productName);
|
|
206
|
+
onlineCount = await getOnlineStatsProductOnlineCount(befly, productName);
|
|
207
|
+
} else {
|
|
208
|
+
currentDay = await getOnlineStatsPeriodData(befly, "day", reportDate);
|
|
209
|
+
weekCurrent = await getOnlineStatsPeriodData(befly, "week", weekStartDate);
|
|
210
|
+
monthCurrent = await getOnlineStatsPeriodData(befly, "month", monthStartDate);
|
|
211
|
+
onlineCount = await getOnlineStatsTotalOnlineCount(befly);
|
|
218
212
|
}
|
|
219
213
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return b.totalPv - a.totalPv;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return String(a.productName).localeCompare(String(b.productName), "zh-CN");
|
|
226
|
-
});
|
|
214
|
+
const projectLists = hasProductFilter ? [] : befly.config.projectLists;
|
|
215
|
+
const products = await buildOnlineStatsProducts(befly, recentDateList, reportDate, weekStartDate, monthStartDate, projectLists);
|
|
227
216
|
|
|
228
217
|
return befly.tool.Yes("获取成功", {
|
|
229
218
|
queryTime: now,
|
package/checks/config.js
CHANGED
|
@@ -10,6 +10,47 @@ z.config(z.locales.zhCN());
|
|
|
10
10
|
const boolIntSchema = z.union([z.literal(0), z.literal(1), z.literal(true), z.literal(false)]);
|
|
11
11
|
const noTrimString = z.string().refine(isNoTrimStringAllowEmpty, "不允许首尾空格");
|
|
12
12
|
const beflyModeSchema = z.union([z.literal("manual"), z.literal("auto")]);
|
|
13
|
+
const projectListCodeSchema = z.string().regex(/^[a-z][a-zA-Z0-9]*$/, "必须是小驼峰命名");
|
|
14
|
+
const projectListItemSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
code: projectListCodeSchema,
|
|
17
|
+
name: noTrimString.min(1),
|
|
18
|
+
productName: noTrimString.min(1),
|
|
19
|
+
productCode: noTrimString.min(1),
|
|
20
|
+
productVersion: noTrimString.optional()
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
const projectListsSchema = z.array(projectListItemSchema).superRefine((projectLists, ctx) => {
|
|
24
|
+
const codeSet = new Set();
|
|
25
|
+
const productCodeSet = new Set();
|
|
26
|
+
|
|
27
|
+
for (let index = 0; index < projectLists.length; index += 1) {
|
|
28
|
+
const code = projectLists[index].code;
|
|
29
|
+
const productCode = projectLists[index].productCode;
|
|
30
|
+
|
|
31
|
+
if (codeSet.has(code)) {
|
|
32
|
+
ctx.addIssue({
|
|
33
|
+
code: z.ZodIssueCode.custom,
|
|
34
|
+
message: `projectLists[${index}].code 重复`,
|
|
35
|
+
path: [index, "code"]
|
|
36
|
+
});
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
codeSet.add(code);
|
|
41
|
+
|
|
42
|
+
if (productCodeSet.has(productCode)) {
|
|
43
|
+
ctx.addIssue({
|
|
44
|
+
code: z.ZodIssueCode.custom,
|
|
45
|
+
message: `projectLists[${index}].productCode 重复`,
|
|
46
|
+
path: [index, "productCode"]
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
productCodeSet.add(productCode);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
13
54
|
|
|
14
55
|
const configSchema = z
|
|
15
56
|
.object({
|
|
@@ -98,7 +139,9 @@ const configSchema = z
|
|
|
98
139
|
skipRoutes: z.array(noTrimString),
|
|
99
140
|
rules: z.array(z.object({}).passthrough())
|
|
100
141
|
})
|
|
101
|
-
.strict()
|
|
142
|
+
.strict(),
|
|
143
|
+
|
|
144
|
+
projectLists: projectListsSchema
|
|
102
145
|
})
|
|
103
146
|
.strict();
|
|
104
147
|
|
package/configs/beflyConfig.json
CHANGED
package/hooks/permission.js
CHANGED
|
@@ -47,9 +47,13 @@ export default {
|
|
|
47
47
|
|
|
48
48
|
// 4. 角色权限检查
|
|
49
49
|
// apiPath 在 apiHandler 中已统一生成并写入 ctx.apiPath
|
|
50
|
+
let hasPermission = false;
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
if (befly.cache && typeof befly.cache.checkRoleApis === "function") {
|
|
53
|
+
hasPermission = await befly.cache.checkRoleApis(ctx.roleCode, ctx.apiPath);
|
|
54
|
+
} else {
|
|
55
|
+
hasPermission = await befly.redis.sismember(`role:apis:${ctx.roleCode}`, ctx.apiPath);
|
|
56
|
+
}
|
|
53
57
|
|
|
54
58
|
if (!hasPermission) {
|
|
55
59
|
ctx.response = ErrorResponse(
|