befly 3.10.18 → 3.11.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/README.md +83 -307
- package/dist/befly.config.d.ts +7 -0
- package/{befly.config.ts → dist/befly.config.js} +11 -36
- package/dist/befly.js +15621 -0
- package/dist/befly.min.js +21 -0
- package/dist/checks/checkApi.d.ts +1 -0
- package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
- package/dist/checks/checkHook.d.ts +1 -0
- package/dist/checks/checkHook.js +86 -0
- package/dist/checks/checkMenu.d.ts +7 -0
- package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
- package/dist/checks/checkPlugin.d.ts +1 -0
- package/dist/checks/checkPlugin.js +86 -0
- package/dist/checks/checkTable.d.ts +6 -0
- package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
- package/dist/configs/presetFields.d.ts +4 -0
- package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
- package/dist/configs/presetRegexp.d.ts +145 -0
- package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
- package/dist/hooks/auth.d.ts +7 -0
- package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
- package/dist/hooks/cors.d.ts +11 -0
- package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
- package/dist/hooks/parser.d.ts +14 -0
- package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
- package/dist/hooks/permission.d.ts +14 -0
- package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
- package/dist/hooks/validator.d.ts +11 -0
- package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
- package/dist/index.d.ts +26 -0
- package/{main.ts → dist/index.js} +61 -100
- package/dist/lib/asyncContext.d.ts +21 -0
- package/dist/lib/asyncContext.js +27 -0
- package/dist/lib/cacheHelper.d.ts +95 -0
- package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
- package/dist/lib/cacheKeys.d.ts +23 -0
- package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
- package/dist/lib/cipher.d.ts +153 -0
- package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
- package/dist/lib/connect.d.ts +91 -0
- package/{lib/connect.ts → dist/lib/connect.js} +47 -88
- package/dist/lib/dbDialect.d.ts +87 -0
- package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
- package/dist/lib/dbHelper.d.ts +204 -0
- package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
- package/dist/lib/dbUtils.d.ts +68 -0
- package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
- package/dist/lib/jwt.d.ts +13 -0
- package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
- package/dist/lib/logger.d.ts +42 -0
- package/dist/lib/logger.js +1144 -0
- package/dist/lib/redisHelper.d.ts +185 -0
- package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
- package/dist/lib/sqlBuilder.d.ts +160 -0
- package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
- package/dist/lib/sqlCheck.d.ts +23 -0
- package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
- package/dist/lib/validator.d.ts +45 -0
- package/{lib/validator.ts → dist/lib/validator.js} +44 -61
- package/dist/loader/loadApis.d.ts +12 -0
- package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
- package/dist/loader/loadHooks.d.ts +7 -0
- package/dist/loader/loadHooks.js +35 -0
- package/dist/loader/loadPlugins.d.ts +8 -0
- package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
- package/dist/paths.d.ts +93 -0
- package/{paths.ts → dist/paths.js} +6 -19
- package/dist/plugins/cache.d.ts +16 -0
- package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
- package/dist/plugins/cipher.d.ts +12 -0
- package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
- package/dist/plugins/config.d.ts +12 -0
- package/dist/plugins/config.js +8 -0
- package/dist/plugins/db.d.ts +16 -0
- package/{plugins/db.ts → dist/plugins/db.js} +11 -17
- package/dist/plugins/jwt.d.ts +12 -0
- package/dist/plugins/jwt.js +12 -0
- package/dist/plugins/logger.d.ts +32 -0
- package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
- package/dist/plugins/redis.d.ts +16 -0
- package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
- package/dist/plugins/tool.d.ts +81 -0
- package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
- package/dist/router/api.d.ts +14 -0
- package/dist/router/api.js +107 -0
- package/dist/router/static.d.ts +9 -0
- package/{router/static.ts → dist/router/static.js} +20 -34
- package/dist/scripts/ensureDist.d.ts +1 -0
- package/dist/scripts/ensureDist.js +296 -0
- package/dist/sync/syncApi.d.ts +3 -0
- package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
- package/dist/sync/syncCache.d.ts +2 -0
- package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
- package/dist/sync/syncDev.d.ts +6 -0
- package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
- package/dist/sync/syncMenu.d.ts +14 -0
- package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
- package/dist/sync/syncTable.d.ts +151 -0
- package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
- package/{types → dist/types}/api.d.ts +12 -51
- package/dist/types/api.js +4 -0
- package/{types → dist/types}/befly.d.ts +32 -227
- package/dist/types/befly.js +4 -0
- package/{types → dist/types}/cache.d.ts +7 -15
- package/dist/types/cache.js +4 -0
- package/dist/types/cipher.d.ts +27 -0
- package/dist/types/cipher.js +7 -0
- package/{types → dist/types}/common.d.ts +8 -33
- package/dist/types/common.js +5 -0
- package/{types → dist/types}/context.d.ts +3 -5
- package/dist/types/context.js +4 -0
- package/{types → dist/types}/crypto.d.ts +0 -3
- package/dist/types/crypto.js +4 -0
- package/dist/types/database.d.ts +138 -0
- package/dist/types/database.js +4 -0
- package/dist/types/hook.d.ts +17 -0
- package/dist/types/hook.js +6 -0
- package/dist/types/jwt.d.ts +75 -0
- package/dist/types/jwt.js +4 -0
- package/dist/types/logger.d.ts +59 -0
- package/dist/types/logger.js +6 -0
- package/dist/types/plugin.d.ts +16 -0
- package/dist/types/plugin.js +6 -0
- package/dist/types/redis.d.ts +71 -0
- package/dist/types/redis.js +4 -0
- package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
- package/dist/types/roleApisCache.js +8 -0
- package/dist/types/sync.d.ts +92 -0
- package/dist/types/sync.js +4 -0
- package/dist/types/table.d.ts +34 -0
- package/dist/types/table.js +4 -0
- package/dist/types/validate.d.ts +67 -0
- package/dist/types/validate.js +4 -0
- package/dist/utils/calcPerfTime.d.ts +4 -0
- package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
- package/dist/utils/convertBigIntFields.d.ts +11 -0
- package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
- package/dist/utils/cors.d.ts +8 -0
- package/{utils/cors.ts → dist/utils/cors.js} +1 -3
- package/dist/utils/disableMenusGlob.d.ts +13 -0
- package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
- package/dist/utils/fieldClear.d.ts +11 -0
- package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
- package/dist/utils/getClientIp.d.ts +6 -0
- package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
- package/dist/utils/importDefault.d.ts +1 -0
- package/dist/utils/importDefault.js +29 -0
- package/dist/utils/isDirentDirectory.d.ts +2 -0
- package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
- package/dist/utils/loadMenuConfigs.d.ts +29 -0
- package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
- package/dist/utils/mergeAndConcat.d.ts +7 -0
- package/dist/utils/mergeAndConcat.js +72 -0
- package/dist/utils/processAtSymbol.d.ts +4 -0
- package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
- package/dist/utils/processInfo.d.ts +24 -0
- package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
- package/dist/utils/response.d.ts +20 -0
- package/{utils/response.ts → dist/utils/response.js} +28 -49
- package/dist/utils/scanAddons.d.ts +17 -0
- package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
- package/dist/utils/scanConfig.d.ts +26 -0
- package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
- package/dist/utils/scanCoreBuiltins.d.ts +3 -0
- package/dist/utils/scanCoreBuiltins.js +65 -0
- package/dist/utils/scanFiles.d.ts +30 -0
- package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
- package/dist/utils/scanSources.d.ts +10 -0
- package/dist/utils/scanSources.js +46 -0
- package/dist/utils/sortModules.d.ts +28 -0
- package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
- package/dist/utils/util.d.ts +84 -0
- package/dist/utils/util.js +262 -0
- package/package.json +26 -34
- package/.gitignore +0 -0
- package/bunfig.toml +0 -3
- package/checks/checkHook.ts +0 -48
- package/checks/checkPlugin.ts +0 -48
- package/configs/presetRegexp.ts +0 -225
- package/docs/README.md +0 -98
- package/docs/api/api.md +0 -1921
- package/docs/guide/examples.md +0 -926
- package/docs/guide/quickstart.md +0 -354
- package/docs/hooks/auth.md +0 -38
- package/docs/hooks/cors.md +0 -28
- package/docs/hooks/hook.md +0 -838
- package/docs/hooks/parser.md +0 -19
- package/docs/hooks/rateLimit.md +0 -47
- package/docs/infra/redis.md +0 -628
- package/docs/plugins/cipher.md +0 -61
- package/docs/plugins/database.md +0 -189
- package/docs/plugins/plugin.md +0 -986
- package/docs/reference/addon.md +0 -510
- package/docs/reference/config.md +0 -573
- package/docs/reference/logger.md +0 -495
- package/docs/reference/sync.md +0 -478
- package/docs/reference/table.md +0 -763
- package/docs/reference/validator.md +0 -620
- package/lib/asyncContext.ts +0 -43
- package/lib/logger.ts +0 -811
- package/loader/loadHooks.ts +0 -51
- package/plugins/config.ts +0 -13
- package/plugins/jwt.ts +0 -15
- package/router/api.ts +0 -130
- package/tsconfig.json +0 -8
- package/types/database.d.ts +0 -541
- package/types/hook.d.ts +0 -25
- package/types/jwt.d.ts +0 -118
- package/types/logger.d.ts +0 -65
- package/types/plugin.d.ts +0 -19
- package/types/redis.d.ts +0 -83
- package/types/sync.d.ts +0 -398
- package/types/table.d.ts +0 -216
- package/types/validate.d.ts +0 -69
- package/utils/arrayKeysToCamel.ts +0 -18
- package/utils/configTypes.ts +0 -3
- package/utils/genShortId.ts +0 -12
- package/utils/importDefault.ts +0 -21
- package/utils/keysToCamel.ts +0 -22
- package/utils/keysToSnake.ts +0 -22
- package/utils/pickFields.ts +0 -19
- package/utils/scanSources.ts +0 -64
- package/utils/sqlLog.ts +0 -37
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 缓存助手 - TypeScript 版本
|
|
3
|
+
* 负责在服务器启动时缓存接口、菜单和角色权限到 Redis
|
|
4
|
+
*/
|
|
5
|
+
type CacheHelperDb = {
|
|
6
|
+
tableExists(table: string): Promise<{
|
|
7
|
+
data: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
getAll(options: any): Promise<{
|
|
10
|
+
data: {
|
|
11
|
+
lists: any[];
|
|
12
|
+
};
|
|
13
|
+
}>;
|
|
14
|
+
};
|
|
15
|
+
type CacheHelperRedis = {
|
|
16
|
+
setObject<T = any>(key: string, obj: T, ttl?: number | null): Promise<string | null>;
|
|
17
|
+
getObject<T = any>(key: string): Promise<T | null>;
|
|
18
|
+
del(key: string): Promise<number>;
|
|
19
|
+
delBatch(keys: string[]): Promise<number>;
|
|
20
|
+
sadd(key: string, members: string[]): Promise<number>;
|
|
21
|
+
saddBatch(items: Array<{
|
|
22
|
+
key: string;
|
|
23
|
+
members: string[];
|
|
24
|
+
}>): Promise<number>;
|
|
25
|
+
smembers(key: string): Promise<string[]>;
|
|
26
|
+
sismember(key: string, member: string): Promise<boolean>;
|
|
27
|
+
};
|
|
28
|
+
type CacheHelperDeps = {
|
|
29
|
+
db: CacheHelperDb;
|
|
30
|
+
redis: CacheHelperRedis;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* 缓存助手类
|
|
34
|
+
*/
|
|
35
|
+
export declare class CacheHelper {
|
|
36
|
+
private db;
|
|
37
|
+
private redis;
|
|
38
|
+
constructor(deps: CacheHelperDeps);
|
|
39
|
+
private assertApiPathname;
|
|
40
|
+
private assertApiPathList;
|
|
41
|
+
/**
|
|
42
|
+
* 缓存所有接口到 Redis
|
|
43
|
+
*/
|
|
44
|
+
cacheApis(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* 缓存所有菜单到 Redis(从数据库读取)
|
|
47
|
+
*/
|
|
48
|
+
cacheMenus(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* 缓存所有角色的接口权限到 Redis
|
|
51
|
+
* 全量重建:清理所有角色权限缓存并重建
|
|
52
|
+
* - 极简方案:每个角色一个 Set,直接覆盖更新(DEL + SADD)
|
|
53
|
+
*/
|
|
54
|
+
rebuildRoleApiPermissions(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* 增量刷新单个角色的接口权限缓存
|
|
57
|
+
* - apiIds 为空数组:仅清理缓存(防止残留)
|
|
58
|
+
* - apiIds 非空:使用 $in 最小查询,DEL 后 SADD
|
|
59
|
+
*/
|
|
60
|
+
refreshRoleApiPermissions(roleCode: string, apiPaths: string[]): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* 缓存所有数据
|
|
63
|
+
*/
|
|
64
|
+
cacheAll(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* 获取缓存的所有接口
|
|
67
|
+
* @returns 接口列表
|
|
68
|
+
*/
|
|
69
|
+
getApis(): Promise<any[]>;
|
|
70
|
+
/**
|
|
71
|
+
* 获取缓存的所有菜单
|
|
72
|
+
* @returns 菜单列表
|
|
73
|
+
*/
|
|
74
|
+
getMenus(): Promise<any[]>;
|
|
75
|
+
/**
|
|
76
|
+
* 获取角色的接口权限
|
|
77
|
+
* @param roleCode - 角色代码
|
|
78
|
+
* @returns 接口路径列表
|
|
79
|
+
*/
|
|
80
|
+
getRolePermissions(roleCode: string): Promise<string[]>;
|
|
81
|
+
/**
|
|
82
|
+
* 检查角色是否有指定接口权限
|
|
83
|
+
* @param roleCode - 角色代码
|
|
84
|
+
* @param apiPath - 接口路径(url.pathname,例如 /api/user/login;与 method 无关)
|
|
85
|
+
* @returns 是否有权限
|
|
86
|
+
*/
|
|
87
|
+
checkRolePermission(roleCode: string, apiPath: string): Promise<boolean>;
|
|
88
|
+
/**
|
|
89
|
+
* 删除角色的接口权限缓存
|
|
90
|
+
* @param roleCode - 角色代码
|
|
91
|
+
* @returns 是否删除成功
|
|
92
|
+
*/
|
|
93
|
+
deleteRolePermissions(roleCode: string): Promise<boolean>;
|
|
94
|
+
}
|
|
95
|
+
export {};
|
|
@@ -2,108 +2,71 @@
|
|
|
2
2
|
* 缓存助手 - TypeScript 版本
|
|
3
3
|
* 负责在服务器启动时缓存接口、菜单和角色权限到 Redis
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import { Logger } from "./logger.ts";
|
|
8
|
-
|
|
9
|
-
type CacheHelperDb = {
|
|
10
|
-
tableExists(table: string): Promise<{ data: boolean }>;
|
|
11
|
-
getAll(options: any): Promise<{ data: { lists: any[] } }>;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
type CacheHelperRedis = {
|
|
15
|
-
setObject<T = any>(key: string, obj: T, ttl?: number | null): Promise<string | null>;
|
|
16
|
-
getObject<T = any>(key: string): Promise<T | null>;
|
|
17
|
-
del(key: string): Promise<number>;
|
|
18
|
-
delBatch(keys: string[]): Promise<number>;
|
|
19
|
-
sadd(key: string, members: string[]): Promise<number>;
|
|
20
|
-
saddBatch(items: Array<{ key: string; members: string[] }>): Promise<number>;
|
|
21
|
-
smembers(key: string): Promise<string[]>;
|
|
22
|
-
sismember(key: string, member: string): Promise<boolean>;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
type CacheHelperDeps = {
|
|
26
|
-
db: CacheHelperDb;
|
|
27
|
-
redis: CacheHelperRedis;
|
|
28
|
-
};
|
|
29
|
-
|
|
5
|
+
import { CacheKeys } from "./cacheKeys";
|
|
6
|
+
import { Logger } from "./logger";
|
|
30
7
|
/**
|
|
31
8
|
* 缓存助手类
|
|
32
9
|
*/
|
|
33
10
|
export class CacheHelper {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
constructor(deps: CacheHelperDeps) {
|
|
11
|
+
db;
|
|
12
|
+
redis;
|
|
13
|
+
constructor(deps) {
|
|
38
14
|
this.db = deps.db;
|
|
39
15
|
this.redis = deps.redis;
|
|
40
16
|
}
|
|
41
|
-
|
|
42
|
-
private assertApiPathname(value: unknown, errorPrefix: string): string {
|
|
17
|
+
assertApiPathname(value, errorPrefix) {
|
|
43
18
|
if (typeof value !== "string") {
|
|
44
19
|
throw new Error(`${errorPrefix} 必须是字符串`);
|
|
45
20
|
}
|
|
46
|
-
|
|
47
21
|
const trimmed = value.trim();
|
|
48
22
|
if (!trimmed) {
|
|
49
23
|
throw new Error(`${errorPrefix} 不允许为空字符串`);
|
|
50
24
|
}
|
|
51
|
-
|
|
52
25
|
if (/^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/i.test(trimmed)) {
|
|
53
26
|
throw new Error(`${errorPrefix} 不允许包含 method 前缀,应为 url.pathname(例如 /api/app/xxx)`);
|
|
54
27
|
}
|
|
55
|
-
|
|
56
28
|
if (!trimmed.startsWith("/")) {
|
|
57
29
|
throw new Error(`${errorPrefix} 必须是 pathname(以 / 开头)`);
|
|
58
30
|
}
|
|
59
|
-
|
|
60
31
|
if (trimmed.includes(" ")) {
|
|
61
32
|
throw new Error(`${errorPrefix} 不允许包含空格`);
|
|
62
33
|
}
|
|
63
|
-
|
|
64
34
|
return trimmed;
|
|
65
35
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
let list: unknown = value;
|
|
71
|
-
|
|
36
|
+
assertApiPathList(value, roleCode) {
|
|
37
|
+
if (value === null || value === undefined)
|
|
38
|
+
return [];
|
|
39
|
+
let list = value;
|
|
72
40
|
// 兼容历史/手工数据:apis 可能被存成 JSON 字符串或 "null"
|
|
73
41
|
if (typeof list === "string") {
|
|
74
42
|
const trimmed = list.trim();
|
|
75
|
-
|
|
76
43
|
// TEXT 字段常见历史值:"null"(表示空)
|
|
77
44
|
if (trimmed === "" || trimmed === "null") {
|
|
78
45
|
return [];
|
|
79
46
|
}
|
|
80
|
-
|
|
81
47
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
82
48
|
try {
|
|
83
49
|
list = JSON.parse(trimmed);
|
|
84
|
-
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
85
52
|
throw new Error(`角色权限数据不合法:addon_admin_role.apis JSON 解析失败,roleCode=${roleCode}`);
|
|
86
53
|
}
|
|
87
54
|
}
|
|
88
55
|
}
|
|
89
|
-
|
|
90
56
|
if (!Array.isArray(list)) {
|
|
91
57
|
const typeLabel = typeof list;
|
|
92
58
|
throw new Error(`角色权限数据不合法:addon_admin_role.apis 必须是字符串数组或 JSON 数组字符串,roleCode=${roleCode},type=${typeLabel}`);
|
|
93
59
|
}
|
|
94
|
-
|
|
95
|
-
const out: string[] = [];
|
|
60
|
+
const out = [];
|
|
96
61
|
for (const item of list) {
|
|
97
62
|
out.push(this.assertApiPathname(item, `角色权限数据不合法:addon_admin_role.apis 元素,roleCode=${roleCode}`));
|
|
98
63
|
}
|
|
99
|
-
|
|
100
64
|
return out;
|
|
101
65
|
}
|
|
102
|
-
|
|
103
66
|
/**
|
|
104
67
|
* 缓存所有接口到 Redis
|
|
105
68
|
*/
|
|
106
|
-
async cacheApis()
|
|
69
|
+
async cacheApis() {
|
|
107
70
|
try {
|
|
108
71
|
// 检查表是否存在
|
|
109
72
|
const tableExists = await this.db.tableExists("addon_admin_api");
|
|
@@ -111,27 +74,24 @@ export class CacheHelper {
|
|
|
111
74
|
Logger.warn("⚠️ 接口表不存在,跳过接口缓存");
|
|
112
75
|
return;
|
|
113
76
|
}
|
|
114
|
-
|
|
115
77
|
// 从数据库查询所有接口
|
|
116
78
|
const apiList = await this.db.getAll({
|
|
117
79
|
table: "addon_admin_api"
|
|
118
80
|
});
|
|
119
|
-
|
|
120
81
|
// 缓存到 Redis
|
|
121
82
|
const result = await this.redis.setObject(CacheKeys.apisAll(), apiList.data.lists);
|
|
122
|
-
|
|
123
83
|
if (result === null) {
|
|
124
84
|
Logger.warn("⚠️ 接口缓存失败");
|
|
125
85
|
}
|
|
126
|
-
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
127
88
|
Logger.error({ err: error }, "⚠️ 接口缓存异常");
|
|
128
89
|
}
|
|
129
90
|
}
|
|
130
|
-
|
|
131
91
|
/**
|
|
132
92
|
* 缓存所有菜单到 Redis(从数据库读取)
|
|
133
93
|
*/
|
|
134
|
-
async cacheMenus()
|
|
94
|
+
async cacheMenus() {
|
|
135
95
|
try {
|
|
136
96
|
// 检查表是否存在
|
|
137
97
|
const tableExists = await this.db.tableExists("addon_admin_menu");
|
|
@@ -139,194 +99,173 @@ export class CacheHelper {
|
|
|
139
99
|
Logger.warn("⚠️ 菜单表不存在,跳过菜单缓存");
|
|
140
100
|
return;
|
|
141
101
|
}
|
|
142
|
-
|
|
143
102
|
// 从数据库查询所有菜单
|
|
144
103
|
const menus = await this.db.getAll({
|
|
145
104
|
table: "addon_admin_menu"
|
|
146
105
|
});
|
|
147
|
-
|
|
148
106
|
// 缓存到 Redis
|
|
149
107
|
const result = await this.redis.setObject(CacheKeys.menusAll(), menus.data.lists);
|
|
150
|
-
|
|
151
108
|
if (result === null) {
|
|
152
109
|
Logger.warn("⚠️ 菜单缓存失败");
|
|
153
110
|
}
|
|
154
|
-
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
155
113
|
Logger.warn({ err: error }, "⚠️ 菜单缓存异常");
|
|
156
114
|
}
|
|
157
115
|
}
|
|
158
|
-
|
|
159
116
|
/**
|
|
160
117
|
* 缓存所有角色的接口权限到 Redis
|
|
161
118
|
* 全量重建:清理所有角色权限缓存并重建
|
|
162
119
|
* - 极简方案:每个角色一个 Set,直接覆盖更新(DEL + SADD)
|
|
163
120
|
*/
|
|
164
|
-
async rebuildRoleApiPermissions()
|
|
121
|
+
async rebuildRoleApiPermissions() {
|
|
165
122
|
try {
|
|
166
123
|
// 检查表是否存在
|
|
167
124
|
const roleTableExists = await this.db.tableExists("addon_admin_role");
|
|
168
|
-
|
|
169
125
|
if (!roleTableExists.data) {
|
|
170
126
|
Logger.warn("⚠️ 角色表不存在,跳过角色权限缓存");
|
|
171
127
|
return;
|
|
172
128
|
}
|
|
173
|
-
|
|
174
129
|
// 查询所有角色(仅取必要字段)
|
|
175
130
|
const roles = await this.db.getAll({
|
|
176
131
|
table: "addon_admin_role",
|
|
177
132
|
fields: ["code", "apis"]
|
|
178
133
|
});
|
|
179
|
-
|
|
180
|
-
const roleApiPathsMap = new Map<string, string[]>();
|
|
181
|
-
|
|
134
|
+
const roleApiPathsMap = new Map();
|
|
182
135
|
for (const role of roles.data.lists) {
|
|
183
|
-
if (!role?.code)
|
|
136
|
+
if (!role?.code)
|
|
137
|
+
continue;
|
|
184
138
|
const apiPaths = this.assertApiPathList(role.apis, role.code);
|
|
185
139
|
roleApiPathsMap.set(role.code, apiPaths);
|
|
186
140
|
}
|
|
187
|
-
|
|
188
141
|
const roleCodes = Array.from(roleApiPathsMap.keys());
|
|
189
142
|
if (roleCodes.length === 0) {
|
|
190
143
|
Logger.info("✅ 没有需要缓存的角色权限");
|
|
191
144
|
return;
|
|
192
145
|
}
|
|
193
|
-
|
|
194
146
|
// 清理所有角色的缓存 key(保证幂等)
|
|
195
147
|
const roleKeys = roleCodes.map((code) => CacheKeys.roleApis(code));
|
|
196
148
|
await this.redis.delBatch(roleKeys);
|
|
197
|
-
|
|
198
149
|
// 批量写入新缓存(只写入非空权限)
|
|
199
|
-
const items
|
|
200
|
-
|
|
150
|
+
const items = [];
|
|
201
151
|
for (const roleCode of roleCodes) {
|
|
202
152
|
const apiPaths = roleApiPathsMap.get(roleCode) || [];
|
|
203
153
|
const members = Array.from(new Set(apiPaths)).sort();
|
|
204
|
-
|
|
205
154
|
if (members.length > 0) {
|
|
206
155
|
items.push({ key: CacheKeys.roleApis(roleCode), members: members });
|
|
207
156
|
}
|
|
208
157
|
}
|
|
209
|
-
|
|
210
158
|
if (items.length > 0) {
|
|
211
159
|
await this.redis.saddBatch(items);
|
|
212
160
|
}
|
|
213
|
-
|
|
214
161
|
// 极简方案不做版本/ready/meta:重建完成即生效
|
|
215
|
-
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
216
164
|
Logger.error({ err: error }, "⚠️ 角色权限缓存异常(将阻断启动)");
|
|
217
165
|
throw error;
|
|
218
166
|
}
|
|
219
167
|
}
|
|
220
|
-
|
|
221
168
|
/**
|
|
222
169
|
* 增量刷新单个角色的接口权限缓存
|
|
223
170
|
* - apiIds 为空数组:仅清理缓存(防止残留)
|
|
224
171
|
* - apiIds 非空:使用 $in 最小查询,DEL 后 SADD
|
|
225
172
|
*/
|
|
226
|
-
async refreshRoleApiPermissions(roleCode
|
|
173
|
+
async refreshRoleApiPermissions(roleCode, apiPaths) {
|
|
227
174
|
if (!roleCode || typeof roleCode !== "string") {
|
|
228
175
|
throw new Error("roleCode 必须是非空字符串");
|
|
229
176
|
}
|
|
230
177
|
if (!Array.isArray(apiPaths)) {
|
|
231
178
|
throw new Error("apiPaths 必须是数组");
|
|
232
179
|
}
|
|
233
|
-
|
|
234
180
|
const normalizedPaths = apiPaths.map((p) => this.assertApiPathname(p, `refreshRoleApiPermissions: apiPaths 元素,roleCode=${roleCode}`));
|
|
235
181
|
const roleKey = CacheKeys.roleApis(roleCode);
|
|
236
|
-
|
|
237
182
|
// 空数组短路:保证清理残留
|
|
238
183
|
if (normalizedPaths.length === 0) {
|
|
239
184
|
await this.redis.del(roleKey);
|
|
240
185
|
return;
|
|
241
186
|
}
|
|
242
|
-
|
|
243
187
|
const members = Array.from(new Set(normalizedPaths));
|
|
244
|
-
|
|
245
188
|
await this.redis.del(roleKey);
|
|
246
189
|
if (members.length > 0) {
|
|
247
190
|
await this.redis.sadd(roleKey, members);
|
|
248
191
|
}
|
|
249
192
|
}
|
|
250
|
-
|
|
251
193
|
/**
|
|
252
194
|
* 缓存所有数据
|
|
253
195
|
*/
|
|
254
|
-
async cacheAll()
|
|
196
|
+
async cacheAll() {
|
|
255
197
|
// 1. 缓存接口
|
|
256
198
|
await this.cacheApis();
|
|
257
|
-
|
|
258
199
|
// 2. 缓存菜单
|
|
259
200
|
await this.cacheMenus();
|
|
260
|
-
|
|
261
201
|
// 3. 缓存角色权限
|
|
262
202
|
await this.rebuildRoleApiPermissions();
|
|
263
203
|
}
|
|
264
|
-
|
|
265
204
|
/**
|
|
266
205
|
* 获取缓存的所有接口
|
|
267
206
|
* @returns 接口列表
|
|
268
207
|
*/
|
|
269
|
-
async getApis()
|
|
208
|
+
async getApis() {
|
|
270
209
|
try {
|
|
271
|
-
const apis = await this.redis.getObject
|
|
210
|
+
const apis = await this.redis.getObject(CacheKeys.apisAll());
|
|
272
211
|
return apis || [];
|
|
273
|
-
}
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
274
214
|
Logger.error({ err: error }, "获取接口缓存失败");
|
|
275
215
|
return [];
|
|
276
216
|
}
|
|
277
217
|
}
|
|
278
|
-
|
|
279
218
|
/**
|
|
280
219
|
* 获取缓存的所有菜单
|
|
281
220
|
* @returns 菜单列表
|
|
282
221
|
*/
|
|
283
|
-
async getMenus()
|
|
222
|
+
async getMenus() {
|
|
284
223
|
try {
|
|
285
|
-
const menus = await this.redis.getObject
|
|
224
|
+
const menus = await this.redis.getObject(CacheKeys.menusAll());
|
|
286
225
|
return menus || [];
|
|
287
|
-
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
288
228
|
Logger.error({ err: error }, "获取菜单缓存失败");
|
|
289
229
|
return [];
|
|
290
230
|
}
|
|
291
231
|
}
|
|
292
|
-
|
|
293
232
|
/**
|
|
294
233
|
* 获取角色的接口权限
|
|
295
234
|
* @param roleCode - 角色代码
|
|
296
235
|
* @returns 接口路径列表
|
|
297
236
|
*/
|
|
298
|
-
async getRolePermissions(roleCode
|
|
237
|
+
async getRolePermissions(roleCode) {
|
|
299
238
|
try {
|
|
300
239
|
const permissions = await this.redis.smembers(CacheKeys.roleApis(roleCode));
|
|
301
240
|
return permissions || [];
|
|
302
|
-
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
303
243
|
Logger.error({ err: error, roleCode: roleCode }, "获取角色权限缓存失败");
|
|
304
244
|
return [];
|
|
305
245
|
}
|
|
306
246
|
}
|
|
307
|
-
|
|
308
247
|
/**
|
|
309
248
|
* 检查角色是否有指定接口权限
|
|
310
249
|
* @param roleCode - 角色代码
|
|
311
250
|
* @param apiPath - 接口路径(url.pathname,例如 /api/user/login;与 method 无关)
|
|
312
251
|
* @returns 是否有权限
|
|
313
252
|
*/
|
|
314
|
-
async checkRolePermission(roleCode
|
|
253
|
+
async checkRolePermission(roleCode, apiPath) {
|
|
315
254
|
try {
|
|
316
255
|
const pathname = this.assertApiPathname(apiPath, "checkRolePermission: apiPath");
|
|
317
256
|
return await this.redis.sismember(CacheKeys.roleApis(roleCode), pathname);
|
|
318
|
-
}
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
319
259
|
Logger.error({ err: error, roleCode: roleCode }, "检查角色权限失败");
|
|
320
260
|
return false;
|
|
321
261
|
}
|
|
322
262
|
}
|
|
323
|
-
|
|
324
263
|
/**
|
|
325
264
|
* 删除角色的接口权限缓存
|
|
326
265
|
* @param roleCode - 角色代码
|
|
327
266
|
* @returns 是否删除成功
|
|
328
267
|
*/
|
|
329
|
-
async deleteRolePermissions(roleCode
|
|
268
|
+
async deleteRolePermissions(roleCode) {
|
|
330
269
|
try {
|
|
331
270
|
const result = await this.redis.del(CacheKeys.roleApis(roleCode));
|
|
332
271
|
if (result > 0) {
|
|
@@ -334,7 +273,8 @@ export class CacheHelper {
|
|
|
334
273
|
return true;
|
|
335
274
|
}
|
|
336
275
|
return false;
|
|
337
|
-
}
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
338
278
|
Logger.error({ err: error, roleCode: roleCode }, "删除角色权限缓存失败");
|
|
339
279
|
return false;
|
|
340
280
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Key 统一管理
|
|
3
|
+
* 所有缓存键在此统一定义,避免硬编码分散
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Cache Key 生成函数集合
|
|
7
|
+
*/
|
|
8
|
+
export declare class CacheKeys {
|
|
9
|
+
/** 所有接口缓存 */
|
|
10
|
+
static apisAll(): string;
|
|
11
|
+
/** 所有菜单缓存 */
|
|
12
|
+
static menusAll(): string;
|
|
13
|
+
/** 角色信息缓存(完整角色对象) */
|
|
14
|
+
static roleInfo(roleCode: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* 角色接口权限缓存(Set 集合)
|
|
17
|
+
* - key: role:apis:${roleCode}
|
|
18
|
+
* - member: url.pathname(例如 /api/user/login;与 method 无关)
|
|
19
|
+
*/
|
|
20
|
+
static roleApis(roleCode: string): string;
|
|
21
|
+
/** 表结构缓存 */
|
|
22
|
+
static tableColumns(table: string): string;
|
|
23
|
+
}
|
|
@@ -2,37 +2,32 @@
|
|
|
2
2
|
* Cache Key 统一管理
|
|
3
3
|
* 所有缓存键在此统一定义,避免硬编码分散
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
* Cache Key 生成函数集合
|
|
8
7
|
*/
|
|
9
8
|
export class CacheKeys {
|
|
10
9
|
/** 所有接口缓存 */
|
|
11
|
-
static apisAll()
|
|
10
|
+
static apisAll() {
|
|
12
11
|
return "apis:all";
|
|
13
12
|
}
|
|
14
|
-
|
|
15
13
|
/** 所有菜单缓存 */
|
|
16
|
-
static menusAll()
|
|
14
|
+
static menusAll() {
|
|
17
15
|
return "menus:all";
|
|
18
16
|
}
|
|
19
|
-
|
|
20
17
|
/** 角色信息缓存(完整角色对象) */
|
|
21
|
-
static roleInfo(roleCode
|
|
18
|
+
static roleInfo(roleCode) {
|
|
22
19
|
return `role:info:${roleCode}`;
|
|
23
20
|
}
|
|
24
|
-
|
|
25
21
|
/**
|
|
26
22
|
* 角色接口权限缓存(Set 集合)
|
|
27
23
|
* - key: role:apis:${roleCode}
|
|
28
24
|
* - member: url.pathname(例如 /api/user/login;与 method 无关)
|
|
29
25
|
*/
|
|
30
|
-
static roleApis(roleCode
|
|
26
|
+
static roleApis(roleCode) {
|
|
31
27
|
return `role:apis:${roleCode}`;
|
|
32
28
|
}
|
|
33
|
-
|
|
34
29
|
/** 表结构缓存 */
|
|
35
|
-
static tableColumns(table
|
|
30
|
+
static tableColumns(table) {
|
|
36
31
|
return `table:columns:${table}`;
|
|
37
32
|
}
|
|
38
33
|
}
|