befly 3.17.0 → 3.17.2

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.
Files changed (150) hide show
  1. package/README.md +6 -0
  2. package/apis/admin/cacheRefresh.js +122 -0
  3. package/apis/admin/del.js +34 -0
  4. package/apis/admin/detail.js +23 -0
  5. package/apis/admin/ins.js +69 -0
  6. package/apis/admin/list.js +28 -0
  7. package/apis/admin/upd.js +95 -0
  8. package/apis/api/all.js +24 -0
  9. package/apis/api/list.js +31 -0
  10. package/apis/auth/login.js +123 -0
  11. package/apis/auth/sendSmsCode.js +24 -0
  12. package/apis/dashboard/configStatus.js +39 -0
  13. package/apis/dashboard/environmentInfo.js +43 -0
  14. package/apis/dashboard/performanceMetrics.js +20 -0
  15. package/apis/dashboard/permissionStats.js +27 -0
  16. package/apis/dashboard/serviceStatus.js +75 -0
  17. package/apis/dashboard/systemInfo.js +19 -0
  18. package/apis/dashboard/systemOverview.js +30 -0
  19. package/apis/dashboard/systemResources.js +106 -0
  20. package/apis/dict/all.js +23 -0
  21. package/apis/dict/del.js +16 -0
  22. package/apis/dict/detail.js +27 -0
  23. package/apis/dict/ins.js +51 -0
  24. package/apis/dict/items.js +30 -0
  25. package/apis/dict/list.js +36 -0
  26. package/apis/dict/upd.js +74 -0
  27. package/apis/dictType/all.js +16 -0
  28. package/apis/dictType/del.js +38 -0
  29. package/apis/dictType/detail.js +20 -0
  30. package/apis/dictType/ins.js +37 -0
  31. package/apis/dictType/list.js +26 -0
  32. package/apis/dictType/upd.js +51 -0
  33. package/apis/email/config.js +25 -0
  34. package/apis/email/logList.js +23 -0
  35. package/apis/email/send.js +66 -0
  36. package/apis/email/verify.js +21 -0
  37. package/apis/loginLog/list.js +23 -0
  38. package/apis/menu/all.js +41 -0
  39. package/apis/menu/list.js +25 -0
  40. package/apis/operateLog/list.js +23 -0
  41. package/apis/role/all.js +21 -0
  42. package/apis/role/apiSave.js +43 -0
  43. package/apis/role/apis.js +22 -0
  44. package/apis/role/del.js +49 -0
  45. package/apis/role/detail.js +32 -0
  46. package/apis/role/ins.js +46 -0
  47. package/apis/role/list.js +27 -0
  48. package/apis/role/menuSave.js +42 -0
  49. package/apis/role/menus.js +22 -0
  50. package/apis/role/save.js +40 -0
  51. package/apis/role/upd.js +50 -0
  52. package/apis/sysConfig/all.js +16 -0
  53. package/apis/sysConfig/del.js +36 -0
  54. package/apis/sysConfig/get.js +49 -0
  55. package/apis/sysConfig/ins.js +50 -0
  56. package/apis/sysConfig/list.js +24 -0
  57. package/apis/sysConfig/upd.js +62 -0
  58. package/checks/api.js +55 -0
  59. package/checks/config.js +107 -0
  60. package/checks/hook.js +38 -0
  61. package/checks/menu.js +58 -0
  62. package/checks/plugin.js +38 -0
  63. package/checks/table.js +78 -0
  64. package/configs/beflyConfig.json +61 -0
  65. package/configs/beflyMenus.json +85 -0
  66. package/configs/constConfig.js +34 -0
  67. package/configs/regexpAlias.json +55 -0
  68. package/hooks/auth.js +34 -0
  69. package/hooks/cors.js +39 -0
  70. package/hooks/parser.js +90 -0
  71. package/hooks/permission.js +71 -0
  72. package/hooks/validator.js +43 -0
  73. package/index.js +326 -0
  74. package/lib/cacheHelper.js +483 -0
  75. package/lib/cacheKeys.js +42 -0
  76. package/lib/connect.js +120 -0
  77. package/lib/dbHelper/builders.js +698 -0
  78. package/lib/dbHelper/context.js +131 -0
  79. package/lib/dbHelper/dataOps.js +505 -0
  80. package/lib/dbHelper/execute.js +65 -0
  81. package/lib/dbHelper/index.js +27 -0
  82. package/lib/dbHelper/transaction.js +43 -0
  83. package/lib/dbHelper/util.js +58 -0
  84. package/lib/dbHelper/validate.js +549 -0
  85. package/lib/emailHelper.js +110 -0
  86. package/lib/logger.js +604 -0
  87. package/lib/redisHelper.js +684 -0
  88. package/lib/sqlBuilder/batch.js +113 -0
  89. package/lib/sqlBuilder/check.js +150 -0
  90. package/lib/sqlBuilder/compiler.js +347 -0
  91. package/lib/sqlBuilder/errors.js +60 -0
  92. package/lib/sqlBuilder/index.js +218 -0
  93. package/lib/sqlBuilder/parser.js +296 -0
  94. package/lib/sqlBuilder/util.js +260 -0
  95. package/lib/validator.js +303 -0
  96. package/package.json +19 -12
  97. package/paths.js +112 -0
  98. package/plugins/cache.js +16 -0
  99. package/plugins/config.js +11 -0
  100. package/plugins/email.js +27 -0
  101. package/plugins/logger.js +20 -0
  102. package/plugins/mysql.js +36 -0
  103. package/plugins/redis.js +34 -0
  104. package/plugins/tool.js +155 -0
  105. package/router/api.js +140 -0
  106. package/router/static.js +71 -0
  107. package/sql/admin.sql +18 -0
  108. package/sql/api.sql +12 -0
  109. package/sql/dict.sql +13 -0
  110. package/sql/dictType.sql +12 -0
  111. package/sql/emailLog.sql +20 -0
  112. package/sql/loginLog.sql +25 -0
  113. package/sql/menu.sql +12 -0
  114. package/sql/operateLog.sql +22 -0
  115. package/sql/role.sql +14 -0
  116. package/sql/sysConfig.sql +16 -0
  117. package/sync/api.js +93 -0
  118. package/sync/cache.js +13 -0
  119. package/sync/dev.js +171 -0
  120. package/sync/menu.js +99 -0
  121. package/tables/admin.json +56 -0
  122. package/tables/api.json +26 -0
  123. package/tables/dict.json +30 -0
  124. package/tables/dictType.json +24 -0
  125. package/tables/emailLog.json +61 -0
  126. package/tables/loginLog.json +86 -0
  127. package/tables/menu.json +24 -0
  128. package/tables/operateLog.json +68 -0
  129. package/tables/role.json +32 -0
  130. package/tables/sysConfig.json +43 -0
  131. package/utils/calcPerfTime.js +13 -0
  132. package/utils/cors.js +17 -0
  133. package/utils/deepMerge.js +78 -0
  134. package/utils/fieldClear.js +65 -0
  135. package/utils/formatYmdHms.js +23 -0
  136. package/utils/formatZodIssues.js +109 -0
  137. package/utils/getClientIp.js +47 -0
  138. package/utils/importDefault.js +51 -0
  139. package/utils/is.js +462 -0
  140. package/utils/loggerUtils.js +185 -0
  141. package/utils/processInfo.js +39 -0
  142. package/utils/regexpUtil.js +52 -0
  143. package/utils/response.js +114 -0
  144. package/utils/scanFiles.js +124 -0
  145. package/utils/scanSources.js +68 -0
  146. package/utils/sortModules.js +75 -0
  147. package/utils/toSessionTtlSeconds.js +14 -0
  148. package/utils/util.js +374 -0
  149. package/befly.js +0 -16413
  150. package/befly.min.js +0 -72
package/paths.js ADDED
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Befly 框架路径配置
3
+ *
4
+ * 提供统一的路径常量,供整个框架使用
5
+ * 所有路径常量采用具名导出方式,避免通过对象访问
6
+ *
7
+ * 路径分类:
8
+ * - root* 系列:Core 框架内部路径(packages/core/*)
9
+ * - app* 系列:用户项目路径(process.cwd()/*)
10
+ *
11
+ */
12
+
13
+ import { fileURLToPath } from "node:url";
14
+
15
+ import { dirname, join, normalize } from "pathe";
16
+
17
+ // 当前文件的路径信息
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+
21
+ // ==================== Core 框架路径 ====================
22
+
23
+ /**
24
+ * Core 框架根目录
25
+ * @description packages/core/
26
+ */
27
+ export const coreDir = __dirname;
28
+
29
+ /**
30
+ * Core 框架 dist 目录
31
+ * @description 源码态为 packages/core/dist;dist 运行态为 packages/core/dist
32
+ */
33
+ export const coreDistDir = normalize(__dirname).endsWith("/dist") ? __dirname : join(__dirname, "dist");
34
+
35
+ /**
36
+ * Core 框架检查目录
37
+ * @description packages/core/checks/
38
+ * @usage 存放启动检查模块(返回 boolean 的 default 函数)
39
+ */
40
+ export const coreCheckDir = join(__dirname, "checks");
41
+
42
+ /**
43
+ * Core 框架插件目录
44
+ * @description packages/core/plugins/
45
+ * @usage 存放内置插件(mysql, logger, redis, tool 等)
46
+ */
47
+ export const corePluginDir = join(__dirname, "plugins");
48
+
49
+ /**
50
+ * Core 框架钩子目录
51
+ * @description packages/core/hooks/
52
+ * @usage 存放内置钩子(auth, cors, parser 等)
53
+ */
54
+ export const coreHookDir = join(__dirname, "hooks");
55
+
56
+ /**
57
+ * Core 框架 API 目录
58
+ * @description packages/core/apis/
59
+ * @usage 存放框架级别的 API 接口
60
+ */
61
+ export const coreApiDir = join(__dirname, "apis");
62
+
63
+ /**
64
+ * Core 框架表定义目录
65
+ * @description packages/core/tables/
66
+ * @usage 存放框架核心表定义(JSON 格式)
67
+ */
68
+ export const coreTableDir = join(__dirname, "tables");
69
+
70
+ // ==================== 用户项目路径 ====================
71
+
72
+ /**
73
+ * 项目根目录
74
+ * @description process.cwd()
75
+ * @usage 用户项目的根目录
76
+ */
77
+ export const appDir = process.cwd();
78
+
79
+ /**
80
+ * 项目检查目录
81
+ * @description {appDir}/checks/
82
+ * @usage 存放用户自定义启动检查模块
83
+ */
84
+ export const appCheckDir = join(appDir, "checks");
85
+
86
+ /**
87
+ * 项目插件目录
88
+ * @description {appDir}/plugins/
89
+ * @usage 存放用户自定义插件
90
+ */
91
+ export const appPluginDir = join(appDir, "plugins");
92
+
93
+ /**
94
+ * 项目钩子目录
95
+ * @description {appDir}/hooks/
96
+ * @usage 存放用户自定义钩子
97
+ */
98
+ export const appHookDir = join(appDir, "hooks");
99
+
100
+ /**
101
+ * 项目 API 目录
102
+ * @description {appDir}/apis/
103
+ * @usage 存放用户业务 API 接口
104
+ */
105
+ export const appApiDir = join(appDir, "apis");
106
+
107
+ /**
108
+ * 项目表定义目录
109
+ * @description {appDir}/tables/
110
+ * @usage 存放用户业务表定义(JSON 格式)
111
+ */
112
+ export const appTableDir = join(appDir, "tables");
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 缓存插件 - JavaScript 版本
3
+ * 负责在服务器启动时缓存接口、菜单和角色权限到 Redis
4
+ */
5
+
6
+ import { CacheHelper } from "../lib/cacheHelper.js";
7
+
8
+ /**
9
+ * 缓存插件
10
+ */
11
+ export default {
12
+ deps: ["logger", "redis", "mysql"],
13
+ async handler(befly) {
14
+ return new CacheHelper({ mysql: befly.mysql, redis: befly.redis });
15
+ }
16
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 配置插件
3
+ * 提供访问项目配置的能力
4
+ */
5
+
6
+ export default {
7
+ deps: [],
8
+ handler: (befly) => {
9
+ return befly.config;
10
+ }
11
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 邮件插件
3
+ * 提供邮件发送功能,支持 SMTP 配置
4
+ */
5
+
6
+ import { EmailHelper } from "../lib/emailHelper.js";
7
+
8
+ /** 默认配置 */
9
+ const defaultConfig = {
10
+ host: "smtp.qq.com",
11
+ port: 465,
12
+ secure: true,
13
+ user: "",
14
+ pass: "",
15
+ fromName: "Befly System"
16
+ };
17
+
18
+ export default {
19
+ deps: ["mysql", "logger", "config"],
20
+ async handler(befly) {
21
+ // 从 befly.config.email 获取配置
22
+ const emailConfigRaw = (befly.config ? befly.config["email"] : undefined) || {};
23
+ const emailConfig = Object.assign({}, defaultConfig, emailConfigRaw);
24
+
25
+ return new EmailHelper(befly, emailConfig);
26
+ }
27
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 日志插件
3
+ * 提供全局日志功能
4
+ */
5
+
6
+ import { Logger } from "../lib/logger.js";
7
+
8
+ /**
9
+ * 日志插件
10
+ */
11
+ export default {
12
+ deps: [],
13
+ async handler(befly) {
14
+ // 配置 Logger
15
+ if (befly.config && befly.config.logger) {
16
+ Logger.configure(befly.config.logger);
17
+ }
18
+ return Logger;
19
+ }
20
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 数据库插件
3
+ * 初始化数据库连接和 SQL 管理器
4
+ */
5
+
6
+ import { Connect } from "../lib/connect.js";
7
+ import { DbHelper } from "../lib/dbHelper/index.js";
8
+ import { Logger } from "../lib/logger.js";
9
+
10
+ /**
11
+ * 数据库插件
12
+ */
13
+ export default {
14
+ deps: ["logger", "redis"],
15
+ async handler(befly) {
16
+ try {
17
+ // 创建数据库管理器实例
18
+ const dbManager = new DbHelper({
19
+ redis: befly.redis,
20
+ dbName: befly.config?.mysql?.database,
21
+ sql: Connect.getMysql(),
22
+ idMode: befly.config?.mysql?.idMode
23
+ });
24
+
25
+ return dbManager;
26
+ } catch (error) {
27
+ Logger.error("数据库初始化失败", error, { runMode: befly.config?.runMode });
28
+ throw new Error("数据库初始化失败", {
29
+ cause: error,
30
+ code: "runtime",
31
+ subsystem: "db",
32
+ operation: "init"
33
+ });
34
+ }
35
+ }
36
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Redis 插件
3
+ * 初始化 Redis 连接和助手工具
4
+ */
5
+
6
+ import { Connect } from "../lib/connect.js";
7
+ import { Logger } from "../lib/logger.js";
8
+ import { RedisHelper } from "../lib/redisHelper.js";
9
+
10
+ /**
11
+ * Redis 插件
12
+ */
13
+ export default {
14
+ deps: ["logger"],
15
+ async handler(befly) {
16
+ const env = befly.config?.runMode;
17
+ const redisPrefix = befly.config?.redis?.prefix;
18
+ try {
19
+ // 启动期已建立 Redis 连接;这里仅校验连接存在
20
+ Connect.getRedis();
21
+
22
+ // 返回 RedisHelper 实例
23
+ return new RedisHelper(redisPrefix);
24
+ } catch (error) {
25
+ Logger.error("Redis 初始化失败", error, { env: env });
26
+ throw new Error("Redis 初始化失败", {
27
+ cause: error,
28
+ code: "runtime",
29
+ subsystem: "redis",
30
+ operation: "init"
31
+ });
32
+ }
33
+ }
34
+ };
@@ -0,0 +1,155 @@
1
+ /**
2
+ * 工具插件
3
+ * 提供常用的工具函数
4
+ */
5
+
6
+ import { isNumber, isString } from "../utils/is.js";
7
+
8
+ /**
9
+ * 成功响应
10
+ * @param msg - 消息
11
+ * @param data - 数据(可选)
12
+ * @param other - 其他字段(可选)
13
+ * @returns 成功响应对象
14
+ */
15
+ export function Yes(msg, data = {}, other = {}) {
16
+ const out = {
17
+ code: 0,
18
+ msg: msg,
19
+ data: data
20
+ };
21
+ if (other && typeof other === "object") {
22
+ for (const key of Object.keys(other)) {
23
+ out[key] = other[key];
24
+ }
25
+ }
26
+ return out;
27
+ }
28
+
29
+ /**
30
+ * 失败响应
31
+ * @param msg - 消息
32
+ * @param data - 数据(可选)
33
+ * @param other - 其他字段(可选)
34
+ * @returns 失败响应对象
35
+ */
36
+ export function No(msg, data = null, other = {}) {
37
+ const out = {
38
+ code: 1,
39
+ msg: msg,
40
+ data: data
41
+ };
42
+ if (other && typeof other === "object") {
43
+ for (const key of Object.keys(other)) {
44
+ out[key] = other[key];
45
+ }
46
+ }
47
+ return out;
48
+ }
49
+
50
+ /**
51
+ * 统一响应函数
52
+ *
53
+ * 自动识别数据类型并设置正确的 Content-Type:
54
+ * - 对象 → application/json
55
+ * - 字符串 → text/plain
56
+ * - 可通过 options.contentType 手动指定
57
+ *
58
+ * @param ctx 请求上下文
59
+ * @param data 响应数据(对象或字符串)
60
+ * @param options 响应选项
61
+ * @returns Response 对象
62
+ */
63
+ export function Raw(ctx, data, options = {}) {
64
+ const status = isNumber(options.status) ? options.status : 200;
65
+ const contentType = options.contentType;
66
+ const headers = options.headers || {};
67
+
68
+ // 自动判断 Content-Type
69
+ let finalContentType = contentType;
70
+ let body;
71
+
72
+ if (isString(data)) {
73
+ // 字符串类型
74
+ body = data;
75
+ if (!finalContentType) {
76
+ // 自动判断:XML 或纯文本
77
+ finalContentType = data.trim().startsWith("<") ? "application/xml" : "text/plain";
78
+ }
79
+ } else {
80
+ // JSON 序列化(对象/数组/原始值均可)
81
+ body = JSON.stringify(data);
82
+ finalContentType = finalContentType || "application/json";
83
+ }
84
+
85
+ // 合并响应头
86
+ const responseHeaders = {};
87
+ const corsHeaders = ctx.corsHeaders;
88
+ if (corsHeaders && typeof corsHeaders === "object") {
89
+ for (const key of Object.keys(corsHeaders)) {
90
+ responseHeaders[key] = corsHeaders[key];
91
+ }
92
+ }
93
+ responseHeaders["Content-Type"] = finalContentType;
94
+ if (headers && typeof headers === "object") {
95
+ for (const key of Object.keys(headers)) {
96
+ responseHeaders[key] = headers[key];
97
+ }
98
+ }
99
+
100
+ return new Response(body, {
101
+ status: status,
102
+ headers: responseHeaders
103
+ });
104
+ }
105
+
106
+ /**
107
+ * 计算 md5(hex)
108
+ * @param value 输入字符串
109
+ * @returns md5 hex
110
+ */
111
+ export function md5(value) {
112
+ if (!isString(value) || value.length < 1) {
113
+ throw new Error("md5 输入必须是非空字符串", {
114
+ cause: null,
115
+ code: "validation",
116
+ subsystem: "tool",
117
+ operation: "md5"
118
+ });
119
+ }
120
+ const hasher = new Bun.CryptoHasher("md5");
121
+ hasher.update(value);
122
+ return hasher.digest("hex");
123
+ }
124
+
125
+ /**
126
+ * 计算 sha256(hex)
127
+ * @param value 输入字符串
128
+ * @returns sha256 hex
129
+ */
130
+ export function sha256(value) {
131
+ if (!isString(value) || value.length < 1) {
132
+ throw new Error("sha256 输入必须是非空字符串", {
133
+ cause: null,
134
+ code: "validation",
135
+ subsystem: "tool",
136
+ operation: "sha256"
137
+ });
138
+ }
139
+ const hasher = new Bun.CryptoHasher("sha256");
140
+ hasher.update(value);
141
+ return hasher.digest("hex");
142
+ }
143
+
144
+ export default {
145
+ deps: [],
146
+ handler: () => {
147
+ return {
148
+ Yes: Yes,
149
+ No: No,
150
+ Raw: Raw,
151
+ md5: md5,
152
+ sha256: sha256
153
+ };
154
+ }
155
+ };
package/router/api.js ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * API路由处理器
3
+ * 处理 /api/* 路径的请求
4
+ */
5
+
6
+ import { Logger } from "../lib/logger.js";
7
+ // 相对导入
8
+ import { getClientIp } from "../utils/getClientIp.js";
9
+ import { FinalResponse } from "../utils/response.js";
10
+ import { genShortId } from "../utils/util.js";
11
+
12
+ /**
13
+ * API处理器工厂函数
14
+ * @param apis - API路由映射表
15
+ * @param hooks - 钩子列表
16
+ * @param context - 应用上下文
17
+ */
18
+ export function apiHandler(apis, hooks, context) {
19
+ return async (req, server) => {
20
+ // 1. 生成请求 ID
21
+ const requestId = genShortId();
22
+
23
+ // 2. 创建请求上下文
24
+ const url = new URL(req.url);
25
+ // 只用接口路径做存在性判断与匹配:不要把 method 拼进 key
26
+ // 说明:apisMap 的 key 来源于 scanFiles/loadApis 生成的 path(例如 /api/core/xxx)
27
+
28
+ const clientIp = getClientIp(req, server);
29
+
30
+ const now = Date.now();
31
+
32
+ const ctx = {
33
+ method: req.method,
34
+ body: {},
35
+ req: req,
36
+ now: now,
37
+ ip: clientIp,
38
+ headers: req.headers,
39
+ requestId: requestId,
40
+ corsHeaders: {
41
+ "X-Request-ID": requestId
42
+ }
43
+ };
44
+
45
+ const apiData = apis[url.pathname];
46
+ if (apiData) {
47
+ ctx.apiPath = apiData.apiPath;
48
+ ctx.apiName = apiData.name;
49
+ ctx.filePath = apiData.filePath;
50
+ ctx.handler = apiData.handler;
51
+ ctx.method = apiData.method;
52
+ ctx.body = apiData.body;
53
+ ctx.auth = apiData.auth;
54
+ ctx.fields = apiData.fields;
55
+ ctx.required = apiData.required;
56
+ }
57
+
58
+ try {
59
+ // 4. 串联执行所有钩子
60
+ for (const hook of hooks) {
61
+ await hook.handler(context, ctx);
62
+
63
+ // 如果钩子已经设置了 response,停止执行
64
+ if (ctx.response) {
65
+ return ctx.response;
66
+ }
67
+ }
68
+
69
+ // hooks 全部通过后记录请求日志(拦截请求仅由 ErrorResponse 记录)
70
+ if (ctx.handler && req.method !== "OPTIONS") {
71
+ const logData = {
72
+ event: "request",
73
+ requestId: requestId,
74
+ method: req.method,
75
+ apiPath: ctx.apiPath,
76
+ apiName: ctx.apiName,
77
+ ip: clientIp,
78
+ now: now,
79
+ userId: ctx.userId,
80
+ nickname: ctx.nickname,
81
+ roleCode: ctx.roleCode,
82
+ roleType: ctx.roleType
83
+ };
84
+
85
+ if (ctx.body && Object.keys(ctx.body).length > 0) {
86
+ logData["body"] = ctx.body;
87
+ }
88
+
89
+ Logger.info("请求", logData);
90
+ }
91
+
92
+ // 5. 执行 API handler
93
+ if (!ctx.handler) {
94
+ if (req.method !== "OPTIONS") {
95
+ ctx.response = Response.json(
96
+ {
97
+ code: 1,
98
+ msg: "接口不存在"
99
+ },
100
+ {
101
+ headers: ctx.corsHeaders
102
+ }
103
+ );
104
+ }
105
+ } else if (ctx.handler) {
106
+ const result = await ctx.handler(context, ctx);
107
+
108
+ if (result instanceof Response) {
109
+ ctx.response = result;
110
+ } else {
111
+ ctx.result = result;
112
+ }
113
+ }
114
+
115
+ // 7. 返回响应(自动处理 response/result/日志)
116
+ return FinalResponse(ctx);
117
+ } catch (err) {
118
+ // 全局错误处理
119
+ const errorPath = ctx.apiPath ? ctx.apiPath : req.url;
120
+ Logger.error("请求错误", err, {
121
+ path: errorPath,
122
+ requestId: requestId,
123
+ method: req.method,
124
+ apiPath: ctx.apiPath,
125
+ apiName: ctx.apiName,
126
+ ip: clientIp,
127
+ now: now,
128
+ userId: ctx.userId,
129
+ nickname: ctx.nickname,
130
+ roleCode: ctx.roleCode,
131
+ roleType: ctx.roleType
132
+ });
133
+ ctx.result = {
134
+ code: 1,
135
+ msg: "内部服务错误"
136
+ };
137
+ return FinalResponse(ctx);
138
+ }
139
+ };
140
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 静态文件路由处理器
3
+ * 处理 /* 路径的静态文件请求
4
+ */
5
+
6
+ // 外部依赖
7
+ import { join } from "pathe";
8
+
9
+ import { Logger } from "../lib/logger.js";
10
+ // 相对导入
11
+ import { appDir } from "../paths.js";
12
+ import { setCorsOptions } from "../utils/cors.js";
13
+
14
+ /**
15
+ * 静态文件处理器工厂
16
+ */
17
+ export function staticHandler(corsConfig = undefined) {
18
+ return async (req) => {
19
+ // 设置 CORS 响应头
20
+ const corsHeaders = setCorsOptions(req, corsConfig);
21
+
22
+ const url = new URL(req.url);
23
+ const filePath = join(appDir, "public", url.pathname);
24
+
25
+ try {
26
+ // OPTIONS预检请求
27
+ if (req.method === "OPTIONS") {
28
+ return new Response(null, {
29
+ status: 204,
30
+ headers: corsHeaders
31
+ });
32
+ }
33
+
34
+ const file = Bun.file(filePath);
35
+ if (await file.exists()) {
36
+ const headers = {};
37
+ if (corsHeaders && typeof corsHeaders === "object") {
38
+ for (const key of Object.keys(corsHeaders)) {
39
+ headers[key] = corsHeaders[key];
40
+ }
41
+ }
42
+ headers["Content-Type"] = file.type || "application/octet-stream";
43
+ return new Response(file, {
44
+ headers: headers
45
+ });
46
+ } else {
47
+ return Response.json(
48
+ {
49
+ code: 1,
50
+ msg: "文件未找到"
51
+ },
52
+ {
53
+ headers: corsHeaders
54
+ }
55
+ );
56
+ }
57
+ } catch (error) {
58
+ Logger.error("静态文件处理失败", error);
59
+
60
+ return Response.json(
61
+ {
62
+ code: 1,
63
+ msg: "文件读取失败"
64
+ },
65
+ {
66
+ headers: corsHeaders
67
+ }
68
+ );
69
+ }
70
+ };
71
+ }
package/sql/admin.sql ADDED
@@ -0,0 +1,18 @@
1
+ CREATE TABLE IF NOT EXISTS `admin` (
2
+ `id` BIGINT NOT NULL,
3
+ `nickname` VARCHAR(50) NOT NULL DEFAULT '',
4
+ `username` VARCHAR(30) NOT NULL DEFAULT '',
5
+ `password` VARCHAR(500) NOT NULL DEFAULT '',
6
+ `email` VARCHAR(100) NOT NULL DEFAULT '',
7
+ `phone` VARCHAR(20) NOT NULL DEFAULT '',
8
+ `avatar` VARCHAR(500) NOT NULL DEFAULT '',
9
+ `role_code` VARCHAR(50) NOT NULL DEFAULT '',
10
+ `role_type` VARCHAR(5) NOT NULL DEFAULT 'user',
11
+ `last_login_time` BIGINT NOT NULL DEFAULT 0,
12
+ `last_login_ip` VARCHAR(50) NOT NULL DEFAULT '',
13
+ `state` TINYINT NOT NULL DEFAULT 1,
14
+ `created_at` BIGINT NOT NULL DEFAULT 0,
15
+ `updated_at` BIGINT NOT NULL DEFAULT 0,
16
+ `deleted_at` BIGINT NULL DEFAULT NULL,
17
+ PRIMARY KEY (`id`)
18
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
package/sql/api.sql ADDED
@@ -0,0 +1,12 @@
1
+ CREATE TABLE IF NOT EXISTS `api` (
2
+ `id` BIGINT NOT NULL,
3
+ `name` VARCHAR(100) NOT NULL DEFAULT '',
4
+ `auth` VARCHAR(200) NOT NULL DEFAULT '',
5
+ `path` VARCHAR(200) NOT NULL DEFAULT '',
6
+ `parent_path` VARCHAR(200) NOT NULL DEFAULT '',
7
+ `state` TINYINT NOT NULL DEFAULT 1,
8
+ `created_at` BIGINT NOT NULL DEFAULT 0,
9
+ `updated_at` BIGINT NOT NULL DEFAULT 0,
10
+ `deleted_at` BIGINT NULL DEFAULT NULL,
11
+ PRIMARY KEY (`id`)
12
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
package/sql/dict.sql ADDED
@@ -0,0 +1,13 @@
1
+ CREATE TABLE IF NOT EXISTS `dict` (
2
+ `id` BIGINT NOT NULL,
3
+ `type_code` VARCHAR(50) NOT NULL DEFAULT '',
4
+ `key` VARCHAR(50) NOT NULL DEFAULT '',
5
+ `label` VARCHAR(100) NOT NULL DEFAULT '',
6
+ `sort` INT NOT NULL DEFAULT 0,
7
+ `remark` VARCHAR(200) NOT NULL DEFAULT '',
8
+ `state` TINYINT NOT NULL DEFAULT 1,
9
+ `created_at` BIGINT NOT NULL DEFAULT 0,
10
+ `updated_at` BIGINT NOT NULL DEFAULT 0,
11
+ `deleted_at` BIGINT NULL DEFAULT NULL,
12
+ PRIMARY KEY (`id`)
13
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -0,0 +1,12 @@
1
+ CREATE TABLE IF NOT EXISTS `dict_type` (
2
+ `id` BIGINT NOT NULL,
3
+ `code` VARCHAR(50) NOT NULL DEFAULT '',
4
+ `name` VARCHAR(50) NOT NULL DEFAULT '',
5
+ `description` VARCHAR(200) NOT NULL DEFAULT '',
6
+ `sort` INT NOT NULL DEFAULT 0,
7
+ `state` TINYINT NOT NULL DEFAULT 1,
8
+ `created_at` BIGINT NOT NULL DEFAULT 0,
9
+ `updated_at` BIGINT NOT NULL DEFAULT 0,
10
+ `deleted_at` BIGINT NULL DEFAULT NULL,
11
+ PRIMARY KEY (`id`)
12
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;