befly 3.10.17 → 3.10.19
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} +8 -31
- package/dist/checks/checkApi.d.ts +1 -0
- package/{checks/checkApi.ts → dist/checks/checkApi.js} +10 -27
- package/dist/checks/checkHook.d.ts +1 -0
- package/{checks/checkHook.ts → dist/checks/checkHook.js} +10 -19
- package/dist/checks/checkMenu.d.ts +7 -0
- package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +15 -50
- package/dist/checks/checkPlugin.d.ts +1 -0
- package/{checks/checkPlugin.ts → dist/checks/checkPlugin.js} +10 -19
- package/dist/checks/checkTable.d.ts +6 -0
- package/{checks/checkTable.ts → dist/checks/checkTable.js} +16 -40
- 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 +5 -0
- package/{hooks/auth.ts → dist/hooks/auth.js} +5 -9
- package/dist/hooks/cors.d.ts +9 -0
- package/{hooks/cors.ts → dist/hooks/cors.js} +2 -12
- package/dist/hooks/parser.d.ts +12 -0
- package/{hooks/parser.ts → dist/hooks/parser.js} +27 -42
- package/dist/hooks/permission.d.ts +12 -0
- package/{hooks/permission.ts → dist/hooks/permission.js} +11 -22
- package/dist/hooks/validator.d.ts +9 -0
- package/{hooks/validator.ts → dist/hooks/validator.js} +5 -12
- 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} +43 -103
- 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} +46 -87
- 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} +73 -230
- package/dist/lib/dbUtils.d.ts +68 -0
- package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +49 -123
- package/dist/lib/jwt.d.ts +13 -0
- package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
- package/dist/lib/logger.d.ts +32 -0
- package/{lib/logger.ts → dist/lib/logger.js} +201 -278
- package/dist/lib/redisHelper.d.ts +185 -0
- package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +95 -139
- package/dist/lib/sqlBuilder.d.ts +160 -0
- package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +131 -277
- 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} +43 -60
- package/dist/loader/loadApis.d.ts +12 -0
- package/{loader/loadApis.ts → dist/loader/loadApis.js} +7 -17
- package/dist/loader/loadHooks.d.ts +8 -0
- package/{loader/loadHooks.ts → dist/loader/loadHooks.js} +5 -19
- package/dist/loader/loadPlugins.d.ts +8 -0
- package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +8 -20
- package/dist/main.d.ts +26 -0
- package/{main.ts → dist/main.js} +39 -78
- package/dist/paths.d.ts +93 -0
- package/{paths.ts → dist/paths.js} +6 -19
- package/dist/plugins/cache.d.ts +14 -0
- package/{plugins/cache.ts → dist/plugins/cache.js} +4 -11
- package/dist/plugins/cipher.d.ts +10 -0
- package/{plugins/cipher.ts → dist/plugins/cipher.js} +1 -5
- package/dist/plugins/config.d.ts +10 -0
- package/dist/plugins/config.js +6 -0
- package/dist/plugins/db.d.ts +14 -0
- package/{plugins/db.ts → dist/plugins/db.js} +5 -13
- package/dist/plugins/jwt.d.ts +10 -0
- package/{plugins/jwt.ts → dist/plugins/jwt.js} +2 -7
- package/dist/plugins/logger.d.ts +28 -0
- package/{plugins/logger.ts → dist/plugins/logger.js} +2 -7
- package/dist/plugins/redis.d.ts +14 -0
- package/{plugins/redis.ts → dist/plugins/redis.js} +4 -9
- package/dist/plugins/tool.d.ts +79 -0
- package/{plugins/tool.ts → dist/plugins/tool.js} +7 -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} +17 -31
- package/dist/scripts/ensureDist.d.ts +1 -0
- package/dist/scripts/ensureDist.js +80 -0
- package/dist/sync/syncApi.d.ts +3 -0
- package/{sync/syncApi.ts → dist/sync/syncApi.js} +33 -53
- 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} +27 -60
- package/dist/sync/syncMenu.d.ts +14 -0
- package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +61 -121
- package/dist/sync/syncTable.d.ts +151 -0
- package/{sync/syncTable.ts → dist/sync/syncTable.js} +168 -375
- package/{types → dist/types}/api.d.ts +12 -51
- package/dist/types/api.js +4 -0
- package/{types → dist/types}/befly.d.ts +32 -223
- 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 +15 -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 +47 -0
- package/dist/types/logger.js +6 -0
- package/dist/types/plugin.d.ts +14 -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/arrayKeysToCamel.d.ts +13 -0
- package/{utils/arrayKeysToCamel.ts → dist/utils/arrayKeysToCamel.js} +4 -4
- package/dist/utils/calcPerfTime.d.ts +4 -0
- package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
- package/dist/utils/configTypes.d.ts +1 -0
- package/dist/utils/configTypes.js +1 -0
- 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/genShortId.d.ts +10 -0
- package/{utils/genShortId.ts → dist/utils/genShortId.js} +1 -1
- 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/keysToCamel.d.ts +10 -0
- package/{utils/keysToCamel.ts → dist/utils/keysToCamel.js} +4 -5
- package/dist/utils/keysToSnake.d.ts +10 -0
- package/{utils/keysToSnake.ts → dist/utils/keysToSnake.js} +4 -5
- package/dist/utils/loadMenuConfigs.d.ts +5 -0
- package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +22 -49
- package/dist/utils/pickFields.d.ts +4 -0
- package/{utils/pickFields.ts → dist/utils/pickFields.js} +2 -5
- package/dist/utils/process.d.ts +24 -0
- package/{utils/process.ts → dist/utils/process.js} +2 -18
- package/dist/utils/processFields.d.ts +4 -0
- package/{utils/processFields.ts → dist/utils/processFields.js} +4 -8
- package/dist/utils/regex.d.ts +145 -0
- package/{configs/presetRegexp.ts → dist/utils/regex.js} +8 -31
- package/dist/utils/response.d.ts +20 -0
- package/{utils/response.ts → dist/utils/response.js} +27 -48
- package/dist/utils/scanAddons.d.ts +17 -0
- package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +4 -38
- package/dist/utils/scanConfig.d.ts +26 -0
- package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +22 -59
- package/dist/utils/scanFiles.d.ts +30 -0
- package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +25 -65
- package/dist/utils/scanSources.d.ts +10 -0
- package/{utils/scanSources.ts → dist/utils/scanSources.js} +16 -39
- package/dist/utils/sortModules.d.ts +28 -0
- package/{utils/sortModules.ts → dist/utils/sortModules.js} +24 -64
- package/dist/utils/sqlLog.d.ts +14 -0
- package/{utils/sqlLog.ts → dist/utils/sqlLog.js} +2 -14
- package/package.json +15 -32
- package/.gitignore +0 -0
- package/bunfig.toml +0 -3
- 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/plugins/config.ts +0 -13
- package/router/api.ts +0 -130
- package/tsconfig.json +0 -54
- 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/configTypes.ts +0 -3
- package/utils/importDefault.ts +0 -21
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
import type { Dirent } from "node:fs";
|
|
2
|
-
|
|
3
1
|
import { statSync } from "node:fs";
|
|
4
|
-
|
|
5
2
|
import { join } from "pathe";
|
|
6
|
-
|
|
7
|
-
export const isDirentDirectory = (parentDir: string, entry: Dirent): boolean => {
|
|
3
|
+
export const isDirentDirectory = (parentDir, entry) => {
|
|
8
4
|
if (entry.isDirectory()) {
|
|
9
5
|
return true;
|
|
10
6
|
}
|
|
11
|
-
|
|
12
7
|
// 兼容 Windows 下的 junction / workspace link:Dirent.isDirectory() 可能为 false,但它实际指向目录。
|
|
13
8
|
if (!entry.isSymbolicLink()) {
|
|
14
9
|
return false;
|
|
15
10
|
}
|
|
16
|
-
|
|
17
11
|
try {
|
|
18
12
|
const stats = statSync(join(parentDir, entry.name));
|
|
19
13
|
return stats.isDirectory();
|
|
20
|
-
}
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
21
16
|
return false;
|
|
22
17
|
}
|
|
23
18
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 对象字段名转小驼峰
|
|
3
|
+
* @param obj - 源对象
|
|
4
|
+
* @returns 字段名转为小驼峰格式的新对象
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* keysToCamel({ user_id: 123, user_name: 'John' }) // { userId: 123, userName: 'John' }
|
|
8
|
+
* keysToCamel({ created_at: 1697452800000 }) // { createdAt: 1697452800000 }
|
|
9
|
+
*/
|
|
10
|
+
export declare const keysToCamel: <T = any>(obj: Record<string, any>) => T;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/compat";
|
|
2
2
|
import { camelCase } from "es-toolkit/string";
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* 对象字段名转小驼峰
|
|
6
5
|
* @param obj - 源对象
|
|
@@ -10,10 +9,10 @@ import { camelCase } from "es-toolkit/string";
|
|
|
10
9
|
* keysToCamel({ user_id: 123, user_name: 'John' }) // { userId: 123, userName: 'John' }
|
|
11
10
|
* keysToCamel({ created_at: 1697452800000 }) // { createdAt: 1697452800000 }
|
|
12
11
|
*/
|
|
13
|
-
export const keysToCamel =
|
|
14
|
-
if (!obj || !isPlainObject(obj))
|
|
15
|
-
|
|
16
|
-
const result
|
|
12
|
+
export const keysToCamel = (obj) => {
|
|
13
|
+
if (!obj || !isPlainObject(obj))
|
|
14
|
+
return obj;
|
|
15
|
+
const result = {};
|
|
17
16
|
for (const [key, value] of Object.entries(obj)) {
|
|
18
17
|
const camelKey = camelCase(key);
|
|
19
18
|
result[camelKey] = value;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 对象字段名转下划线
|
|
3
|
+
* @param obj - 源对象
|
|
4
|
+
* @returns 字段名转为下划线格式的新对象
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* keysToSnake({ userId: 123, userName: 'John' }) // { user_id: 123, user_name: 'John' }
|
|
8
|
+
* keysToSnake({ createdAt: 1697452800000 }) // { created_at: 1697452800000 }
|
|
9
|
+
*/
|
|
10
|
+
export declare const keysToSnake: <T = any>(obj: Record<string, any>) => T;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/compat";
|
|
2
2
|
import { snakeCase } from "es-toolkit/string";
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* 对象字段名转下划线
|
|
6
5
|
* @param obj - 源对象
|
|
@@ -10,10 +9,10 @@ import { snakeCase } from "es-toolkit/string";
|
|
|
10
9
|
* keysToSnake({ userId: 123, userName: 'John' }) // { user_id: 123, user_name: 'John' }
|
|
11
10
|
* keysToSnake({ createdAt: 1697452800000 }) // { created_at: 1697452800000 }
|
|
12
11
|
*/
|
|
13
|
-
export const keysToSnake =
|
|
14
|
-
if (!obj || !isPlainObject(obj))
|
|
15
|
-
|
|
16
|
-
const result
|
|
12
|
+
export const keysToSnake = (obj) => {
|
|
13
|
+
if (!obj || !isPlainObject(obj))
|
|
14
|
+
return obj;
|
|
15
|
+
const result = {};
|
|
17
16
|
for (const [key, value] of Object.entries(obj)) {
|
|
18
17
|
const snakeKey = snakeCase(key);
|
|
19
18
|
result[snakeKey] = value;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { MenuConfig } from "../types/sync.ts";
|
|
2
|
+
import type { AddonInfo } from "./scanAddons.ts";
|
|
3
|
+
export declare function scanViewsDirToMenuConfigs(viewsDir: string, prefix: string, parentPath?: string): Promise<MenuConfig[]>;
|
|
4
|
+
export declare function getParentPath(path: string): string;
|
|
5
|
+
export declare function loadMenuConfigs(addons: AddonInfo[]): Promise<MenuConfig[]>;
|
|
@@ -1,90 +1,69 @@
|
|
|
1
|
-
import type { MenuConfig } from "../types/sync.js";
|
|
2
|
-
import type { AddonInfo } from "./scanAddons.js";
|
|
3
|
-
import type { ViewDirMeta } from "befly-shared/utils/scanViewsDir";
|
|
4
|
-
|
|
5
1
|
import { existsSync } from "node:fs";
|
|
6
2
|
import { readdir, readFile } from "node:fs/promises";
|
|
7
|
-
|
|
8
3
|
import { cleanDirName, extractDefinePageMetaFromScriptSetup, extractScriptSetupBlock } from "befly-shared/utils/scanViewsDir";
|
|
9
4
|
import { join } from "pathe";
|
|
10
|
-
|
|
11
5
|
import { Logger } from "../lib/logger.js";
|
|
12
6
|
import { isDirentDirectory } from "./isDirentDirectory.js";
|
|
13
|
-
|
|
14
|
-
export async function scanViewsDirToMenuConfigs(viewsDir: string, prefix: string, parentPath: string = ""): Promise<MenuConfig[]> {
|
|
7
|
+
export async function scanViewsDirToMenuConfigs(viewsDir, prefix, parentPath = "") {
|
|
15
8
|
if (!existsSync(viewsDir)) {
|
|
16
9
|
return [];
|
|
17
10
|
}
|
|
18
|
-
|
|
19
|
-
const menus: MenuConfig[] = [];
|
|
11
|
+
const menus = [];
|
|
20
12
|
const entries = await readdir(viewsDir, { withFileTypes: true });
|
|
21
|
-
|
|
22
13
|
for (const entry of entries) {
|
|
23
14
|
if (!isDirentDirectory(viewsDir, entry) || entry.name === "components") {
|
|
24
15
|
continue;
|
|
25
16
|
}
|
|
26
|
-
|
|
27
17
|
const dirPath = join(viewsDir, entry.name);
|
|
28
18
|
const indexVuePath = join(dirPath, "index.vue");
|
|
29
|
-
|
|
30
19
|
if (!existsSync(indexVuePath)) {
|
|
31
20
|
continue;
|
|
32
21
|
}
|
|
33
|
-
|
|
34
|
-
let meta: ViewDirMeta | null = null;
|
|
22
|
+
let meta = null;
|
|
35
23
|
try {
|
|
36
24
|
const content = await readFile(indexVuePath, "utf-8");
|
|
37
|
-
|
|
38
25
|
const scriptSetup = extractScriptSetupBlock(content);
|
|
39
26
|
if (!scriptSetup) {
|
|
40
27
|
Logger.warn({ path: indexVuePath }, "index.vue 缺少 <script setup>,已跳过该目录菜单同步");
|
|
41
28
|
continue;
|
|
42
29
|
}
|
|
43
|
-
|
|
44
30
|
meta = extractDefinePageMetaFromScriptSetup(scriptSetup);
|
|
45
31
|
if (!meta?.title) {
|
|
46
32
|
Logger.warn({ path: indexVuePath }, "index.vue 未声明 definePage({ meta: { title, order? } }),已跳过该目录菜单同步");
|
|
47
33
|
continue;
|
|
48
34
|
}
|
|
49
|
-
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
50
37
|
Logger.warn({ err: error, path: indexVuePath }, "读取 index.vue 失败");
|
|
51
38
|
continue;
|
|
52
39
|
}
|
|
53
|
-
|
|
54
40
|
if (!meta?.title) {
|
|
55
41
|
continue;
|
|
56
42
|
}
|
|
57
|
-
|
|
58
43
|
const cleanName = cleanDirName(entry.name);
|
|
59
|
-
let menuPath
|
|
44
|
+
let menuPath;
|
|
60
45
|
if (cleanName === "index") {
|
|
61
46
|
menuPath = parentPath;
|
|
62
|
-
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
63
49
|
menuPath = parentPath ? `${parentPath}/${cleanName}` : `/${cleanName}`;
|
|
64
50
|
}
|
|
65
|
-
|
|
66
51
|
const fullPath = prefix ? (menuPath ? `${prefix}${menuPath}` : prefix) : menuPath || "/";
|
|
67
|
-
|
|
68
|
-
const menu: MenuConfig = {
|
|
52
|
+
const menu = {
|
|
69
53
|
name: meta.title,
|
|
70
54
|
path: fullPath,
|
|
71
55
|
sort: meta.order ?? 999999
|
|
72
56
|
};
|
|
73
|
-
|
|
74
57
|
const children = await scanViewsDirToMenuConfigs(dirPath, prefix, menuPath);
|
|
75
58
|
if (children.length > 0) {
|
|
76
59
|
menu.children = children;
|
|
77
60
|
}
|
|
78
|
-
|
|
79
61
|
menus.push(menu);
|
|
80
62
|
}
|
|
81
|
-
|
|
82
63
|
menus.sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999));
|
|
83
|
-
|
|
84
64
|
return menus;
|
|
85
65
|
}
|
|
86
|
-
|
|
87
|
-
export function getParentPath(path: string): string {
|
|
66
|
+
export function getParentPath(path) {
|
|
88
67
|
// "/a/b" => "/a"
|
|
89
68
|
// "/a" => ""
|
|
90
69
|
const parts = path.split("/").filter((p) => !!p);
|
|
@@ -93,10 +72,8 @@ export function getParentPath(path: string): string {
|
|
|
93
72
|
}
|
|
94
73
|
return `/${parts.slice(0, -1).join("/")}`;
|
|
95
74
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const allMenus: MenuConfig[] = [];
|
|
99
|
-
|
|
75
|
+
export async function loadMenuConfigs(addons) {
|
|
76
|
+
const allMenus = [];
|
|
100
77
|
for (const addon of addons) {
|
|
101
78
|
const adminViewsDirByTopLevel = join(addon.fullPath, "adminViews");
|
|
102
79
|
const adminViewsDirByViews = join(addon.fullPath, "views", "admin");
|
|
@@ -104,7 +81,6 @@ export async function loadMenuConfigs(addons: AddonInfo[]): Promise<MenuConfig[]
|
|
|
104
81
|
if (!adminViewsDir) {
|
|
105
82
|
continue;
|
|
106
83
|
}
|
|
107
|
-
|
|
108
84
|
try {
|
|
109
85
|
const prefix = `/${addon.source}/${addon.name}`;
|
|
110
86
|
const menus = await scanViewsDirToMenuConfigs(adminViewsDir, prefix);
|
|
@@ -113,19 +89,16 @@ export async function loadMenuConfigs(addons: AddonInfo[]): Promise<MenuConfig[]
|
|
|
113
89
|
allMenus.push(menu);
|
|
114
90
|
}
|
|
115
91
|
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
"扫描 addon views 目录失败"
|
|
125
|
-
);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
Logger.warn({
|
|
95
|
+
err: error,
|
|
96
|
+
addon: addon.name,
|
|
97
|
+
addonSource: addon.source,
|
|
98
|
+
dir: adminViewsDir
|
|
99
|
+
}, "扫描 addon views 目录失败");
|
|
126
100
|
}
|
|
127
101
|
}
|
|
128
|
-
|
|
129
102
|
const menusJsonPath = join(process.cwd(), "menus.json");
|
|
130
103
|
if (existsSync(menusJsonPath)) {
|
|
131
104
|
try {
|
|
@@ -136,10 +109,10 @@ export async function loadMenuConfigs(addons: AddonInfo[]): Promise<MenuConfig[]
|
|
|
136
109
|
allMenus.push(menu);
|
|
137
110
|
}
|
|
138
111
|
}
|
|
139
|
-
}
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
140
114
|
Logger.warn({ err: error }, "读取项目 menus.json 失败");
|
|
141
115
|
}
|
|
142
116
|
}
|
|
143
|
-
|
|
144
117
|
return allMenus;
|
|
145
118
|
}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/compat";
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* 挑选指定字段
|
|
5
4
|
*/
|
|
6
|
-
export const pickFields =
|
|
5
|
+
export const pickFields = (obj, keys) => {
|
|
7
6
|
if (!obj || (!isPlainObject(obj) && !Array.isArray(obj))) {
|
|
8
7
|
return {};
|
|
9
8
|
}
|
|
10
|
-
|
|
11
|
-
const result: any = {};
|
|
9
|
+
const result = {};
|
|
12
10
|
for (const key of keys) {
|
|
13
11
|
if (key in obj) {
|
|
14
12
|
result[key] = obj[key];
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
|
-
|
|
18
15
|
return result;
|
|
19
16
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 进程角色信息
|
|
3
|
+
*/
|
|
4
|
+
export interface ProcessRole {
|
|
5
|
+
/** 进程角色:primary(主进程)或 worker(工作进程) */
|
|
6
|
+
role: "primary" | "worker";
|
|
7
|
+
/** 实例 ID(PM2 或 Bun Worker) */
|
|
8
|
+
instanceId: string | null;
|
|
9
|
+
/** 运行环境:bun-cluster、pm2-cluster 或 standalone */
|
|
10
|
+
env: "bun-cluster" | "pm2-cluster" | "standalone";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 获取当前进程角色信息
|
|
14
|
+
* @returns 进程角色、实例 ID 和运行环境
|
|
15
|
+
*/
|
|
16
|
+
export declare function getProcessRole(): ProcessRole;
|
|
17
|
+
/**
|
|
18
|
+
* 检测当前进程是否为主进程
|
|
19
|
+
* 用于集群模式下避免重复执行同步任务
|
|
20
|
+
* - Bun 集群:BUN_WORKER_ID 为空时是主进程
|
|
21
|
+
* - PM2 集群:PM2_INSTANCE_ID 为 '0' 或不存在时是主进程
|
|
22
|
+
* @returns 是否为主进程
|
|
23
|
+
*/
|
|
24
|
+
export declare function isPrimaryProcess(): boolean;
|
|
@@ -1,23 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 进程角色信息
|
|
3
|
-
*/
|
|
4
|
-
export interface ProcessRole {
|
|
5
|
-
/** 进程角色:primary(主进程)或 worker(工作进程) */
|
|
6
|
-
role: "primary" | "worker";
|
|
7
|
-
/** 实例 ID(PM2 或 Bun Worker) */
|
|
8
|
-
instanceId: string | null;
|
|
9
|
-
/** 运行环境:bun-cluster、pm2-cluster 或 standalone */
|
|
10
|
-
env: "bun-cluster" | "pm2-cluster" | "standalone";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
1
|
/**
|
|
14
2
|
* 获取当前进程角色信息
|
|
15
3
|
* @returns 进程角色、实例 ID 和运行环境
|
|
16
4
|
*/
|
|
17
|
-
export function getProcessRole()
|
|
5
|
+
export function getProcessRole() {
|
|
18
6
|
const bunWorkerId = process.env.BUN_WORKER_ID;
|
|
19
7
|
const pm2InstanceId = process.env.PM2_INSTANCE_ID;
|
|
20
|
-
|
|
21
8
|
// Bun 集群模式
|
|
22
9
|
if (bunWorkerId !== undefined) {
|
|
23
10
|
return {
|
|
@@ -26,7 +13,6 @@ export function getProcessRole(): ProcessRole {
|
|
|
26
13
|
env: "bun-cluster"
|
|
27
14
|
};
|
|
28
15
|
}
|
|
29
|
-
|
|
30
16
|
// PM2 集群模式
|
|
31
17
|
if (pm2InstanceId !== undefined) {
|
|
32
18
|
return {
|
|
@@ -35,7 +21,6 @@ export function getProcessRole(): ProcessRole {
|
|
|
35
21
|
env: "pm2-cluster"
|
|
36
22
|
};
|
|
37
23
|
}
|
|
38
|
-
|
|
39
24
|
// 单进程模式
|
|
40
25
|
return {
|
|
41
26
|
role: "primary",
|
|
@@ -43,7 +28,6 @@ export function getProcessRole(): ProcessRole {
|
|
|
43
28
|
env: "standalone"
|
|
44
29
|
};
|
|
45
30
|
}
|
|
46
|
-
|
|
47
31
|
/**
|
|
48
32
|
* 检测当前进程是否为主进程
|
|
49
33
|
* 用于集群模式下避免重复执行同步任务
|
|
@@ -51,6 +35,6 @@ export function getProcessRole(): ProcessRole {
|
|
|
51
35
|
* - PM2 集群:PM2_INSTANCE_ID 为 '0' 或不存在时是主进程
|
|
52
36
|
* @returns 是否为主进程
|
|
53
37
|
*/
|
|
54
|
-
export function isPrimaryProcess()
|
|
38
|
+
export function isPrimaryProcess() {
|
|
55
39
|
return getProcessRole().role === "primary";
|
|
56
40
|
}
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import { presetFields } from "../configs/presetFields.js";
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* 处理字段定义:将 @ 符号引用替换为实际字段定义
|
|
5
4
|
*/
|
|
6
|
-
export function processFields(fields
|
|
7
|
-
if (!fields || typeof fields !== "object")
|
|
8
|
-
|
|
9
|
-
const processed
|
|
5
|
+
export function processFields(fields, apiName, routePath) {
|
|
6
|
+
if (!fields || typeof fields !== "object")
|
|
7
|
+
return fields;
|
|
8
|
+
const processed = {};
|
|
10
9
|
for (const [key, value] of Object.entries(fields)) {
|
|
11
10
|
if (typeof value === "string" && value.startsWith("@")) {
|
|
12
11
|
if (presetFields[value]) {
|
|
13
12
|
processed[key] = presetFields[value];
|
|
14
13
|
continue;
|
|
15
14
|
}
|
|
16
|
-
|
|
17
15
|
const validKeys = Object.keys(presetFields).join(", ");
|
|
18
16
|
throw new Error(`API [${apiName}] (${routePath}) 字段 [${key}] 引用了未定义的预设字段 "${value}"。可用的预设字段有: ${validKeys}`);
|
|
19
17
|
}
|
|
20
|
-
|
|
21
18
|
processed[key] = value;
|
|
22
19
|
}
|
|
23
|
-
|
|
24
20
|
return processed;
|
|
25
21
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内置正则表达式别名
|
|
3
|
+
* 用于表单验证和数据校验
|
|
4
|
+
* 命名规范:小驼峰格式
|
|
5
|
+
*/
|
|
6
|
+
export declare const RegexAliases: {
|
|
7
|
+
/** 正整数(不含0) */
|
|
8
|
+
readonly number: "^\\d+$";
|
|
9
|
+
/** 整数(含负数) */
|
|
10
|
+
readonly integer: "^-?\\d+$";
|
|
11
|
+
/** 浮点数 */
|
|
12
|
+
readonly float: "^-?\\d+(\\.\\d+)?$";
|
|
13
|
+
/** 正整数(不含0) */
|
|
14
|
+
readonly positive: "^[1-9]\\d*$";
|
|
15
|
+
/** 负整数 */
|
|
16
|
+
readonly negative: "^-\\d+$";
|
|
17
|
+
/** 零 */
|
|
18
|
+
readonly zero: "^0$";
|
|
19
|
+
/** 纯字母 */
|
|
20
|
+
readonly word: "^[a-zA-Z]+$";
|
|
21
|
+
/** 字母和数字 */
|
|
22
|
+
readonly alphanumeric: "^[a-zA-Z0-9]+$";
|
|
23
|
+
/** 字母、数字和下划线 */
|
|
24
|
+
readonly alphanumeric_: "^[a-zA-Z0-9_]+$";
|
|
25
|
+
/** 字母、数字、下划线和短横线 */
|
|
26
|
+
readonly alphanumericDash_: "^[a-zA-Z0-9_-]+$";
|
|
27
|
+
/** 小写字母 */
|
|
28
|
+
readonly lowercase: "^[a-z]+$";
|
|
29
|
+
/** 大写字母 */
|
|
30
|
+
readonly uppercase: "^[A-Z]+$";
|
|
31
|
+
/** 纯中文 */
|
|
32
|
+
readonly chinese: "^[\\u4e00-\\u9fa5]+$";
|
|
33
|
+
/** 中文和字母 */
|
|
34
|
+
readonly chineseWord: "^[\\u4e00-\\u9fa5a-zA-Z]+$";
|
|
35
|
+
/** 邮箱地址 */
|
|
36
|
+
readonly email: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
|
37
|
+
/** 中国大陆手机号 */
|
|
38
|
+
readonly phone: "^1[3-9]\\d{9}$";
|
|
39
|
+
/** 固定电话(区号-号码) */
|
|
40
|
+
readonly telephone: "^0\\d{2,3}-?\\d{7,8}$";
|
|
41
|
+
/** URL 地址 */
|
|
42
|
+
readonly url: "^https?://";
|
|
43
|
+
/** IPv4 地址 */
|
|
44
|
+
readonly ip: "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$";
|
|
45
|
+
/** IPv6 地址 */
|
|
46
|
+
readonly ipv6: "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$";
|
|
47
|
+
/** 域名 */
|
|
48
|
+
readonly domain: "^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$";
|
|
49
|
+
/** UUID */
|
|
50
|
+
readonly uuid: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
|
|
51
|
+
/** 十六进制字符串 */
|
|
52
|
+
readonly hex: "^[0-9a-fA-F]+$";
|
|
53
|
+
/** Base64 编码 */
|
|
54
|
+
readonly base64: "^[A-Za-z0-9+/=]+$";
|
|
55
|
+
/** MD5 哈希 */
|
|
56
|
+
readonly md5: "^[a-f0-9]{32}$";
|
|
57
|
+
/** SHA1 哈希 */
|
|
58
|
+
readonly sha1: "^[a-f0-9]{40}$";
|
|
59
|
+
/** SHA256 哈希 */
|
|
60
|
+
readonly sha256: "^[a-f0-9]{64}$";
|
|
61
|
+
/** 日期 YYYY-MM-DD */
|
|
62
|
+
readonly date: "^\\d{4}-\\d{2}-\\d{2}$";
|
|
63
|
+
/** 时间 HH:MM:SS */
|
|
64
|
+
readonly time: "^\\d{2}:\\d{2}:\\d{2}$";
|
|
65
|
+
/** ISO 日期时间 */
|
|
66
|
+
readonly datetime: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}";
|
|
67
|
+
/** 年份 */
|
|
68
|
+
readonly year: "^\\d{4}$";
|
|
69
|
+
/** 月份 01-12 */
|
|
70
|
+
readonly month: "^(0[1-9]|1[0-2])$";
|
|
71
|
+
/** 日期 01-31 */
|
|
72
|
+
readonly day: "^(0[1-9]|[12]\\d|3[01])$";
|
|
73
|
+
/** 变量名 */
|
|
74
|
+
readonly variable: "^[a-zA-Z_][a-zA-Z0-9_]*$";
|
|
75
|
+
/** 常量名(全大写) */
|
|
76
|
+
readonly constant: "^[A-Z][A-Z0-9_]*$";
|
|
77
|
+
/** 包名(小写+连字符) */
|
|
78
|
+
readonly package: "^[a-z][a-z0-9-]*$";
|
|
79
|
+
/** 中国身份证号(18位) */
|
|
80
|
+
readonly idCard: "^\\d{17}[\\dXx]$";
|
|
81
|
+
/** 护照号 */
|
|
82
|
+
readonly passport: "^[a-zA-Z0-9]{5,17}$";
|
|
83
|
+
/** 银行卡号(16-19位数字) */
|
|
84
|
+
readonly bankCard: "^\\d{16,19}$";
|
|
85
|
+
/** 微信号(6-20位,字母开头,可包含字母、数字、下划线、减号) */
|
|
86
|
+
readonly wechat: "^[a-zA-Z][a-zA-Z0-9_-]{5,19}$";
|
|
87
|
+
/** QQ号(5-11位数字,首位非0) */
|
|
88
|
+
readonly qq: "^[1-9]\\d{4,10}$";
|
|
89
|
+
/** 支付宝账号(手机号或邮箱) */
|
|
90
|
+
readonly alipay: "^(1[3-9]\\d{9}|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$";
|
|
91
|
+
/** 用户名(4-20位,字母开头,可包含字母、数字、下划线) */
|
|
92
|
+
readonly username: "^[a-zA-Z][a-zA-Z0-9_]{3,19}$";
|
|
93
|
+
/** 昵称(2-20位,支持中文、字母、数字) */
|
|
94
|
+
readonly nickname: "^[\\u4e00-\\u9fa5a-zA-Z0-9]{2,20}$";
|
|
95
|
+
/** 弱密码(至少6位) */
|
|
96
|
+
readonly passwordWeak: "^.{6,}$";
|
|
97
|
+
/** 中等密码(至少8位,包含字母和数字) */
|
|
98
|
+
readonly passwordMedium: "^(?=.*[a-zA-Z])(?=.*\\d).{8,}$";
|
|
99
|
+
/** 强密码(至少8位,包含大小写字母、数字和特殊字符) */
|
|
100
|
+
readonly passwordStrong: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*]).{8,}$";
|
|
101
|
+
/** 车牌号(新能源+普通) */
|
|
102
|
+
readonly licensePlate: "^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$";
|
|
103
|
+
/** 邮政编码 */
|
|
104
|
+
readonly postalCode: "^\\d{6}$";
|
|
105
|
+
/** 版本号(语义化版本) */
|
|
106
|
+
readonly semver: "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?(\\+[a-zA-Z0-9.]+)?$";
|
|
107
|
+
/** 颜色值(十六进制) */
|
|
108
|
+
readonly colorHex: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$";
|
|
109
|
+
/** 空字符串 */
|
|
110
|
+
readonly empty: "^$";
|
|
111
|
+
/** 非空 */
|
|
112
|
+
readonly notempty: ".+";
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* 正则别名类型
|
|
116
|
+
*/
|
|
117
|
+
export type RegexAliasName = keyof typeof RegexAliases;
|
|
118
|
+
/**
|
|
119
|
+
* 获取正则表达式字符串
|
|
120
|
+
* @param name 正则别名(以 @ 开头)或自定义正则字符串
|
|
121
|
+
* @returns 正则表达式字符串
|
|
122
|
+
*/
|
|
123
|
+
export declare function getRegex(name: string): string;
|
|
124
|
+
/**
|
|
125
|
+
* 获取编译后的正则表达式对象(带缓存)
|
|
126
|
+
* @param pattern 正则别名或正则字符串
|
|
127
|
+
* @param flags 正则标志(如 'i', 'g')
|
|
128
|
+
* @returns 编译后的 RegExp 对象
|
|
129
|
+
*/
|
|
130
|
+
export declare function getCompiledRegex(pattern: string, flags?: string): RegExp;
|
|
131
|
+
/**
|
|
132
|
+
* 验证值是否匹配正则(使用缓存)
|
|
133
|
+
* @param value 要验证的值
|
|
134
|
+
* @param pattern 正则别名或正则字符串
|
|
135
|
+
* @returns 是否匹配
|
|
136
|
+
*/
|
|
137
|
+
export declare function matchRegex(value: string, pattern: string): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* 清除正则缓存
|
|
140
|
+
*/
|
|
141
|
+
export declare function clearRegexCache(): void;
|
|
142
|
+
/**
|
|
143
|
+
* 获取缓存大小
|
|
144
|
+
*/
|
|
145
|
+
export declare function getRegexCacheSize(): number;
|