befly 3.13.10 → 3.14.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/dist/befly.js CHANGED
@@ -105,16 +105,20 @@ function upperFirst(s) {
105
105
  if (s.length === 0) {
106
106
  return s;
107
107
  }
108
- return s[0].toUpperCase() + s.slice(1);
108
+ return s.charAt(0).toUpperCase() + s.slice(1);
109
109
  }
110
110
  function camelCase(input) {
111
111
  const parts = toWordParts(input);
112
112
  if (parts.length === 0) {
113
113
  return "";
114
114
  }
115
- const first = parts[0].toLowerCase();
115
+ const firstPart = parts[0];
116
+ if (!firstPart) {
117
+ return "";
118
+ }
119
+ const first = firstPart.toLowerCase();
116
120
  const rest = parts.slice(1).map((p) => upperFirst(p.toLowerCase()));
117
- return [first, ...rest].join("");
121
+ return [first].concat(rest).join("");
118
122
  }
119
123
  function normalizeToWords(input) {
120
124
  return String(input).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9]+/g, " ").trim();
@@ -235,7 +239,7 @@ function sanitizeErrorValue(err, options) {
235
239
  message: truncateString(err.message || "", options.maxStringLen)
236
240
  };
237
241
  if (typeof err.stack === "string") {
238
- out.stack = truncateString(err.stack, options.maxStringLen);
242
+ out["stack"] = truncateString(err.stack, options.maxStringLen);
239
243
  }
240
244
  return out;
241
245
  }
@@ -323,8 +327,12 @@ function sanitizeAny(value, options, state, depth, visited) {
323
327
  const entries = Object.entries(obj);
324
328
  const limit = entries.length > options.sanitizeObjectKeys ? options.sanitizeObjectKeys : entries.length;
325
329
  for (let i = 0;i < limit; i++) {
326
- const key = entries[i][0];
327
- const child = entries[i][1];
330
+ const entry = entries[i];
331
+ if (!entry) {
332
+ continue;
333
+ }
334
+ const key = entry[0];
335
+ const child = entry[1];
328
336
  if (isSensitiveKey(key, options.sensitiveKeyMatcher)) {
329
337
  out[key] = "[MASKED]";
330
338
  continue;
@@ -365,10 +373,10 @@ function setCtxUser(userId, roleCode, nickname, roleType) {
365
373
  const store = storage.getStore();
366
374
  if (!store)
367
375
  return;
368
- store.userId = userId;
369
- store.roleCode = roleCode;
370
- store.nickname = nickname;
371
- store.roleType = roleType;
376
+ store.userId = userId === undefined ? null : userId;
377
+ store.roleCode = roleCode === undefined ? null : roleCode;
378
+ store.nickname = nickname === undefined ? null : nickname;
379
+ store.roleType = roleType === undefined ? null : roleType;
372
380
  }
373
381
  var storage;
374
382
  var init_asyncContext = __esm(() => {
@@ -798,8 +806,8 @@ function buildLogLine(level, record) {
798
806
  return `${safeJsonStringify(out)}
799
807
  `;
800
808
  }
801
- if (base.msg === undefined) {
802
- base.msg = "";
809
+ if (base["msg"] === undefined) {
810
+ base["msg"] = "";
803
811
  }
804
812
  return `${safeJsonStringify(base)}
805
813
  `;
@@ -809,7 +817,7 @@ function safeJsonStringify(obj) {
809
817
  return JSON.stringify(obj);
810
818
  } catch {
811
819
  try {
812
- return JSON.stringify({ level: obj.level, time: obj.time, pid: obj.pid, hostname: obj.hostname, msg: "[Unserializable log record]" });
820
+ return JSON.stringify({ level: obj["level"], time: obj["time"], pid: obj["pid"], hostname: obj["hostname"], msg: "[Unserializable log record]" });
813
821
  } catch {
814
822
  return '{"msg":"[Unserializable log record]"}';
815
823
  }
@@ -855,10 +863,10 @@ function metaToObject() {
855
863
  now: meta.now,
856
864
  durationSinceNowMs
857
865
  };
858
- obj.userId = meta.userId;
859
- obj.roleCode = meta.roleCode;
860
- obj.nickname = meta.nickname;
861
- obj.roleType = meta.roleType;
866
+ obj["userId"] = meta.userId;
867
+ obj["roleCode"] = meta.roleCode;
868
+ obj["nickname"] = meta.nickname;
869
+ obj["roleType"] = meta.roleType;
862
870
  return obj;
863
871
  }
864
872
  function mergeMetaIntoObject(input, meta) {
@@ -1309,7 +1317,7 @@ async function scanViewsDirToMenuConfigs(viewsDir, prefix, parentPath = "") {
1309
1317
  return menus;
1310
1318
  }
1311
1319
  function getParentPath(path) {
1312
- const parts = path.split("/").filter((p) => !!p);
1320
+ const parts = path.split("/").filter((p) => Boolean(p));
1313
1321
  if (parts.length <= 1) {
1314
1322
  return "";
1315
1323
  }
@@ -7590,25 +7598,25 @@ async function checkApi(apis) {
7590
7598
  hasError = true;
7591
7599
  continue;
7592
7600
  }
7593
- if (typeof api?.routePath !== "string" || api.routePath.trim() === "") {
7594
- Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 routePath \u5C5E\u6027\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\uFF08\u7531\u7CFB\u7EDF\u751F\u6210\uFF09" }));
7601
+ if (typeof api?.path !== "string" || api.path.trim() === "") {
7602
+ Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 path \u5C5E\u6027\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\uFF08\u7531\u7CFB\u7EDF\u751F\u6210\uFF09" }));
7595
7603
  hasError = true;
7596
7604
  } else {
7597
- const routePath = api.routePath.trim();
7598
- if (/^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/i.test(routePath)) {
7599
- Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 routePath \u4E0D\u5141\u8BB8\u5305\u542B method \u524D\u7F00\uFF0C\u5E94\u4E3A url.pathname\uFF08\u4F8B\u5982 /api/app/xxx\uFF09" }));
7605
+ const path = api.path.trim();
7606
+ if (/^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/i.test(path)) {
7607
+ Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 path \u4E0D\u5141\u8BB8\u5305\u542B method \u524D\u7F00\uFF0C\u5E94\u4E3A url.pathname\uFF08\u4F8B\u5982 /api/app/xxx\uFF09" }));
7600
7608
  hasError = true;
7601
7609
  }
7602
- if (!routePath.startsWith("/api/")) {
7603
- Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 routePath \u5FC5\u987B\u4EE5 /api/ \u5F00\u5934" }));
7610
+ if (!path.startsWith("/api/")) {
7611
+ Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 path \u5FC5\u987B\u4EE5 /api/ \u5F00\u5934" }));
7604
7612
  hasError = true;
7605
7613
  }
7606
- if (routePath.includes(" ")) {
7607
- Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 routePath \u4E0D\u5141\u8BB8\u5305\u542B\u7A7A\u683C" }));
7614
+ if (path.includes(" ")) {
7615
+ Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 path \u4E0D\u5141\u8BB8\u5305\u542B\u7A7A\u683C" }));
7608
7616
  hasError = true;
7609
7617
  }
7610
- if (routePath.includes("/api//")) {
7611
- Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 routePath \u4E0D\u5141\u8BB8\u51FA\u73B0 /api//\uFF08\u91CD\u590D\u659C\u6760\uFF09" }));
7618
+ if (path.includes("/api//")) {
7619
+ Logger.warn(Object.assign({}, omit(api, ["handler"]), { msg: "\u63A5\u53E3\u7684 path \u4E0D\u5141\u8BB8\u51FA\u73B0 /api//\uFF08\u91CD\u590D\u659C\u6760\uFF09" }));
7612
7620
  hasError = true;
7613
7621
  }
7614
7622
  }
@@ -8320,7 +8328,7 @@ var presetFields = {
8320
8328
  };
8321
8329
 
8322
8330
  // utils/processAtSymbol.ts
8323
- function processAtSymbol(fields, apiName, routePath) {
8331
+ function processAtSymbol(fields, apiName, path) {
8324
8332
  if (!fields || typeof fields !== "object")
8325
8333
  return fields;
8326
8334
  const processed = {};
@@ -8331,7 +8339,7 @@ function processAtSymbol(fields, apiName, routePath) {
8331
8339
  continue;
8332
8340
  }
8333
8341
  const validKeys = Object.keys(presetFields).join(", ");
8334
- throw new Error(`API [${apiName}] (${routePath}) \u5B57\u6BB5 [${key}] \u5F15\u7528\u4E86\u672A\u5B9A\u4E49\u7684\u9884\u8BBE\u5B57\u6BB5 "${value}"\u3002\u53EF\u7528\u7684\u9884\u8BBE\u5B57\u6BB5\u6709: ${validKeys}`);
8342
+ throw new Error(`API [${apiName}] (${path}) \u5B57\u6BB5 [${key}] \u5F15\u7528\u4E86\u672A\u5B9A\u4E49\u7684\u9884\u8BBE\u5B57\u6BB5 "${value}"\u3002\u53EF\u7528\u7684\u9884\u8BBE\u5B57\u6BB5\u6709: ${validKeys}`);
8335
8343
  }
8336
8344
  processed[key] = value;
8337
8345
  }
@@ -8342,14 +8350,13 @@ function processAtSymbol(fields, apiName, routePath) {
8342
8350
  async function loadApis(apis) {
8343
8351
  const apisMap = new Map;
8344
8352
  for (const api of apis) {
8345
- const apiType = api.type;
8346
- if (apiType && apiType !== "api") {
8353
+ if (api.type !== "api") {
8347
8354
  continue;
8348
8355
  }
8349
8356
  try {
8350
8357
  const apiRoute = api;
8351
- apiRoute.fields = processAtSymbol(apiRoute.fields || {}, apiRoute.name, apiRoute.routePath);
8352
- apisMap.set(apiRoute.routePath, apiRoute);
8358
+ apiRoute.fields = processAtSymbol(apiRoute.fields || {}, apiRoute.name, apiRoute.path);
8359
+ apisMap.set(apiRoute.path, apiRoute);
8353
8360
  } catch (error) {
8354
8361
  Logger.error({ err: error, api: api.relativePath, file: api.filePath, msg: "\u63A5\u53E3\u52A0\u8F7D\u5931\u8D25" });
8355
8362
  throw error;
@@ -8469,7 +8476,7 @@ async function loadHooks(hooks) {
8469
8476
  hooksMap.push({
8470
8477
  name: hookName,
8471
8478
  enable: true,
8472
- deps: hook.deps,
8479
+ deps: Array.isArray(hook.deps) ? hook.deps : [],
8473
8480
  handler: hook.handler
8474
8481
  });
8475
8482
  }
@@ -8503,7 +8510,7 @@ async function loadPlugins(plugins, context) {
8503
8510
  pluginsMap.push({
8504
8511
  name: pluginName,
8505
8512
  enable: true,
8506
- deps: plugin.deps,
8513
+ deps: Array.isArray(plugin.deps) ? plugin.deps : [],
8507
8514
  handler: plugin.handler
8508
8515
  });
8509
8516
  } catch (error) {
@@ -8642,11 +8649,12 @@ function apiHandler(apis, hooks, context) {
8642
8649
  requestId,
8643
8650
  corsHeaders: {
8644
8651
  "X-Request-ID": requestId
8645
- },
8646
- api: apis.get(apiPath),
8647
- response: undefined,
8648
- result: undefined
8652
+ }
8649
8653
  };
8654
+ const api = apis.get(apiPath);
8655
+ if (api) {
8656
+ ctx.api = api;
8657
+ }
8650
8658
  return withCtx({
8651
8659
  requestId,
8652
8660
  method: req.method,
@@ -8667,9 +8675,9 @@ function apiHandler(apis, hooks, context) {
8667
8675
  apiName: ctx.api.name
8668
8676
  };
8669
8677
  if (ctx.body && Object.keys(ctx.body).length > 0) {
8670
- logData.body = ctx.body;
8678
+ logData["body"] = ctx.body;
8671
8679
  }
8672
- logData.msg = "request";
8680
+ logData["msg"] = "request";
8673
8681
  Logger.info(logData);
8674
8682
  }
8675
8683
  if (!ctx.api) {
@@ -8783,6 +8791,15 @@ function staticHandler(corsConfig = undefined) {
8783
8791
  // sync/syncApi.ts
8784
8792
  init_logger();
8785
8793
  init_util();
8794
+ var getApiParentPath = (apiPath) => {
8795
+ const segments = apiPath.split("/").map((s) => s.trim()).filter((s) => s.length > 0);
8796
+ const seg2 = segments[1] || "";
8797
+ const take = seg2 === "addon" ? 4 : 3;
8798
+ const parentSegments = segments.slice(0, Math.min(take, segments.length));
8799
+ if (parentSegments.length === 0)
8800
+ return "";
8801
+ return `/${parentSegments.join("/")}`;
8802
+ };
8786
8803
  async function syncApi(ctx, apis) {
8787
8804
  const tableName = "addon_admin_api";
8788
8805
  if (!ctx.db) {
@@ -8797,55 +8814,61 @@ async function syncApi(ctx, apis) {
8797
8814
  }
8798
8815
  const allDbApis = await ctx.db.getAll({
8799
8816
  table: tableName,
8800
- fields: ["id", "routePath", "name", "addonName", "auth", "state"],
8817
+ fields: ["id", "path", "parentPath", "name", "addonName", "auth", "state"],
8801
8818
  where: { state$gte: 0 }
8802
8819
  });
8803
8820
  const dbLists = allDbApis.data.lists || [];
8804
- const allDbApiMap = keyBy(dbLists, (item) => item.routePath);
8821
+ const allDbApiMap = keyBy(dbLists, (item) => item.path);
8805
8822
  const insData = [];
8806
8823
  const updData = [];
8807
8824
  const delData = [];
8808
8825
  const apiRouteKeys = new Set;
8809
8826
  for (const api of apis) {
8810
- const apiType = api.type;
8811
- if (apiType && apiType !== "api") {
8827
+ if (api.type !== "api") {
8812
8828
  continue;
8813
8829
  }
8814
- const normalizedAuth = api.auth === 0 || api.auth === false ? 0 : 1;
8815
- const normalizedApi = {
8816
- name: api.name,
8817
- routePath: api.routePath,
8818
- addonName: api.addonName,
8819
- auth: normalizedAuth
8820
- };
8821
- const routePath = normalizedApi.routePath;
8822
- apiRouteKeys.add(routePath);
8823
- const item = allDbApiMap[routePath];
8830
+ const auth = api.auth === false || api.auth === 0 ? 0 : 1;
8831
+ const parentPath = getApiParentPath(api.path);
8832
+ apiRouteKeys.add(api.path);
8833
+ const item = allDbApiMap[api.path];
8824
8834
  if (item) {
8825
- const dbAuth = item.auth === 0 || item.auth === false ? 0 : 1;
8826
- const shouldUpdate = normalizedApi.name !== item.name || normalizedApi.routePath !== item.routePath || normalizedApi.addonName !== item.addonName || normalizedAuth !== dbAuth;
8835
+ const shouldUpdate = api.name !== item.name || api.path !== item.path || api.addonName !== item.addonName || parentPath !== item.parentPath || auth !== item.auth;
8827
8836
  if (shouldUpdate) {
8828
- updData.push({ id: item.id, api: normalizedApi });
8837
+ updData.push({
8838
+ id: item.id,
8839
+ name: api.name,
8840
+ path: api.path,
8841
+ parentPath,
8842
+ addonName: api.addonName,
8843
+ auth
8844
+ });
8829
8845
  }
8830
8846
  } else {
8831
- insData.push(normalizedApi);
8847
+ insData.push({
8848
+ name: api.name,
8849
+ path: api.path,
8850
+ parentPath,
8851
+ addonName: api.addonName,
8852
+ auth
8853
+ });
8832
8854
  }
8833
8855
  }
8834
8856
  for (const record of dbLists) {
8835
- if (!apiRouteKeys.has(record.routePath)) {
8857
+ if (!apiRouteKeys.has(record.path)) {
8836
8858
  delData.push(record.id);
8837
8859
  }
8838
8860
  }
8839
8861
  if (updData.length > 0) {
8840
8862
  try {
8841
- await ctx.db.updBatch(tableName, updData.map((item) => {
8863
+ await ctx.db.updBatch(tableName, updData.map((api) => {
8842
8864
  return {
8843
- id: item.id,
8865
+ id: api.id,
8844
8866
  data: {
8845
- name: item.api.name,
8846
- routePath: item.api.routePath,
8847
- addonName: item.api.addonName,
8848
- auth: item.api.auth === 0 || item.api.auth === false ? 0 : 1
8867
+ name: api.name,
8868
+ path: api.path,
8869
+ parentPath: api.parentPath,
8870
+ addonName: api.addonName,
8871
+ auth: api.auth
8849
8872
  }
8850
8873
  };
8851
8874
  }));
@@ -8858,9 +8881,10 @@ async function syncApi(ctx, apis) {
8858
8881
  await ctx.db.insBatch(tableName, insData.map((api) => {
8859
8882
  return {
8860
8883
  name: api.name,
8861
- routePath: api.routePath,
8884
+ path: api.path,
8885
+ parentPath: api.parentPath,
8862
8886
  addonName: api.addonName,
8863
- auth: api.auth === 0 || api.auth === false ? 0 : 1
8887
+ auth: api.auth
8864
8888
  };
8865
8889
  }));
8866
8890
  } catch (error) {
@@ -9000,7 +9024,6 @@ async function syncDev(ctx, config2 = {}) {
9000
9024
  if (!config2.devPassword) {
9001
9025
  return;
9002
9026
  }
9003
- const devEmail = typeof config2.devEmail === "string" && config2.devEmail.length > 0 ? config2.devEmail : "dev@qq.com";
9004
9027
  if (!ctx.db) {
9005
9028
  throw new Error("syncDev: ctx.db \u672A\u521D\u59CB\u5316\uFF08Db \u63D2\u4EF6\u672A\u52A0\u8F7D\u6216\u6CE8\u5165\u5931\u8D25\uFF09");
9006
9029
  }
@@ -9019,140 +9042,130 @@ async function syncDev(ctx, config2 = {}) {
9019
9042
  Logger.debug(`addon_admin_menu \u8868\u4E0D\u5B58\u5728`);
9020
9043
  return;
9021
9044
  }
9045
+ if (!(await ctx.db.tableExists("addon_admin_api")).data) {
9046
+ Logger.debug(`addon_admin_api \u8868\u4E0D\u5B58\u5728`);
9047
+ return;
9048
+ }
9022
9049
  const allMenus = await ctx.db.getAll({
9023
9050
  table: "addon_admin_menu",
9024
9051
  fields: ["path"],
9025
9052
  where: { state$gte: 0 },
9026
9053
  orderBy: ["id#ASC"]
9027
9054
  });
9028
- const menuPaths = Array.from(new Set((allMenus.data.lists || []).map((m) => typeof m?.path === "string" ? m.path.trim() : "").filter((p) => p.length > 0)));
9029
- const existApi = await ctx.db.tableExists("addon_admin_api");
9030
- let apiPaths = [];
9031
- if (existApi.data) {
9032
- const allApis = await ctx.db.getAll({
9033
- table: "addon_admin_api",
9034
- fields: ["routePath"],
9035
- where: { state$gte: 0 },
9036
- orderBy: ["id#ASC"]
9037
- });
9038
- apiPaths = Array.from(new Set((allApis.data.lists || []).map((a) => {
9039
- if (typeof a?.routePath !== "string") {
9040
- throw new Error("syncDev: addon_admin_api.routePath \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
9041
- }
9042
- const routePath = a.routePath.trim();
9043
- if (routePath.length === 0) {
9044
- throw new Error("syncDev: addon_admin_api.routePath \u4E0D\u5141\u8BB8\u4E3A\u7A7A\u5B57\u7B26\u4E32");
9055
+ const allApis = await ctx.db.getAll({
9056
+ table: "addon_admin_api",
9057
+ fields: ["path"],
9058
+ where: { state$gte: 0 },
9059
+ orderBy: ["id#ASC"]
9060
+ });
9061
+ const devRole = await ctx.db.getOne({
9062
+ table: "addon_admin_role",
9063
+ where: { code: "dev" }
9064
+ });
9065
+ const devRoleData = {
9066
+ code: "dev",
9067
+ name: "\u5F00\u53D1\u8005\u89D2\u8272",
9068
+ description: "\u62E5\u6709\u6240\u6709\u83DC\u5355\u548C\u63A5\u53E3\u6743\u9650\u7684\u5F00\u53D1\u8005\u89D2\u8272",
9069
+ menus: allMenus.data.lists.map((item) => item.path).filter((v) => v),
9070
+ apis: allApis.data.lists.map((item) => item.path).filter((v) => v),
9071
+ sort: 0
9072
+ };
9073
+ if (devRole.data) {
9074
+ await ctx.db.updData({
9075
+ table: "addon_admin_role",
9076
+ where: { code: "dev" },
9077
+ data: {
9078
+ name: devRoleData.name,
9079
+ description: devRoleData.description,
9080
+ menus: devRoleData.menus,
9081
+ apis: devRoleData.apis,
9082
+ sort: devRoleData.sort
9045
9083
  }
9046
- if (!routePath.startsWith("/")) {
9047
- throw new Error(`syncDev: addon_admin_api.routePath \u5FC5\u987B\u662F pathname\uFF08\u4EE5 / \u5F00\u5934\uFF09\uFF0C\u5F53\u524D\u503C=${routePath}`);
9084
+ });
9085
+ } else {
9086
+ await ctx.db.insData({
9087
+ table: "addon_admin_role",
9088
+ data: devRoleData
9089
+ });
9090
+ }
9091
+ const devAdminData = {
9092
+ nickname: "\u5F00\u53D1\u8005",
9093
+ email: config2.devEmail || "dev@qq.com",
9094
+ username: "dev",
9095
+ password: await Cipher.hashPassword(Cipher.sha256(config2.devPassword + "befly")),
9096
+ roleCode: "dev",
9097
+ roleType: "admin"
9098
+ };
9099
+ const devAdmin = await ctx.db.getOne({
9100
+ table: "addon_admin_admin",
9101
+ where: { username: "dev" }
9102
+ });
9103
+ if (devAdmin.data) {
9104
+ await ctx.db.updData({
9105
+ table: "addon_admin_admin",
9106
+ where: { username: "dev" },
9107
+ data: {
9108
+ nickname: devAdminData.nickname,
9109
+ email: devAdminData.email,
9110
+ username: devAdminData.username,
9111
+ password: devAdminData.password,
9112
+ roleCode: devAdminData.roleCode,
9113
+ roleType: devAdminData.roleType
9048
9114
  }
9049
- return routePath;
9050
- })));
9115
+ });
9116
+ } else {
9117
+ await ctx.db.insData({
9118
+ table: "addon_admin_admin",
9119
+ data: devAdminData
9120
+ });
9051
9121
  }
9052
9122
  const roles = [
9053
- {
9054
- code: "dev",
9055
- name: "\u5F00\u53D1\u8005\u89D2\u8272",
9056
- description: "\u62E5\u6709\u6240\u6709\u83DC\u5355\u548C\u63A5\u53E3\u6743\u9650\u7684\u5F00\u53D1\u8005\u89D2\u8272",
9057
- menus: menuPaths,
9058
- apis: apiPaths,
9059
- sort: 0
9060
- },
9061
9123
  {
9062
9124
  code: "user",
9063
9125
  name: "\u7528\u6237\u89D2\u8272",
9064
9126
  description: "\u666E\u901A\u7528\u6237\u89D2\u8272",
9065
- menus: [],
9066
- apis: [],
9067
9127
  sort: 1
9068
9128
  },
9069
9129
  {
9070
9130
  code: "admin",
9071
9131
  name: "\u7BA1\u7406\u5458\u89D2\u8272",
9072
9132
  description: "\u7BA1\u7406\u5458\u89D2\u8272",
9073
- menus: [],
9074
- apis: [],
9075
9133
  sort: 2
9076
9134
  },
9077
9135
  {
9078
9136
  code: "guest",
9079
9137
  name: "\u8BBF\u5BA2\u89D2\u8272",
9080
9138
  description: "\u8BBF\u5BA2\u89D2\u8272",
9081
- menus: [],
9082
- apis: [],
9083
9139
  sort: 3
9084
9140
  }
9085
9141
  ];
9086
- let devRole = null;
9087
9142
  for (const roleConfig of roles) {
9088
9143
  const existingRole = await ctx.db.getOne({
9089
9144
  table: "addon_admin_role",
9090
9145
  where: { code: roleConfig.code }
9091
9146
  });
9092
- if (existingRole.data) {
9093
- const nextMenus = roleConfig.menus;
9094
- const nextApis = roleConfig.apis;
9095
- const existingMenus = Array.isArray(existingRole.data.menus) ? existingRole.data.menus : [];
9096
- const existingApis = Array.isArray(existingRole.data.apis) ? existingRole.data.apis : [];
9097
- const menusChanged = existingMenus.length !== nextMenus.length || existingMenus.some((v, i) => v !== nextMenus[i]);
9098
- const apisChanged = existingApis.length !== nextApis.length || existingApis.some((v, i) => v !== nextApis[i]);
9099
- const hasChanges = existingRole.data.name !== roleConfig.name || existingRole.data.description !== roleConfig.description || menusChanged || apisChanged || existingRole.data.sort !== roleConfig.sort;
9100
- if (hasChanges) {
9101
- await ctx.db.updData({
9102
- table: "addon_admin_role",
9103
- where: { code: roleConfig.code },
9104
- data: {
9105
- name: roleConfig.name,
9106
- description: roleConfig.description,
9107
- menus: roleConfig.menus,
9108
- apis: roleConfig.apis,
9109
- sort: roleConfig.sort
9110
- }
9111
- });
9112
- }
9113
- if (roleConfig.code === "dev") {
9114
- devRole = existingRole.data;
9115
- }
9147
+ if (existingRole.data?.id) {
9148
+ await ctx.db.updData({
9149
+ table: "addon_admin_role",
9150
+ where: { code: roleConfig.code },
9151
+ data: {
9152
+ name: roleConfig.name,
9153
+ description: roleConfig.description,
9154
+ sort: roleConfig.sort
9155
+ }
9156
+ });
9116
9157
  } else {
9117
- const roleId = await ctx.db.insData({
9158
+ await ctx.db.insData({
9118
9159
  table: "addon_admin_role",
9119
- data: roleConfig
9160
+ data: {
9161
+ code: roleConfig.code,
9162
+ name: roleConfig.name,
9163
+ description: roleConfig.description,
9164
+ sort: roleConfig.sort
9165
+ }
9120
9166
  });
9121
- if (roleConfig.code === "dev") {
9122
- devRole = { id: roleId.data };
9123
- }
9124
9167
  }
9125
9168
  }
9126
- if (!devRole) {
9127
- Logger.error("dev \u89D2\u8272\u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5F00\u53D1\u8005\u8D26\u53F7");
9128
- return;
9129
- }
9130
- const sha256Hashed = Cipher.sha256(config2.devPassword + "befly");
9131
- const hashed = await Cipher.hashPassword(sha256Hashed);
9132
- const devData = {
9133
- nickname: "\u5F00\u53D1\u8005",
9134
- email: devEmail,
9135
- username: "dev",
9136
- password: hashed,
9137
- roleCode: "dev",
9138
- roleType: "admin"
9139
- };
9140
- const existing = await ctx.db.getOne({
9141
- table: "addon_admin_admin",
9142
- where: { email: devEmail }
9143
- });
9144
- if (existing.data) {
9145
- await ctx.db.updData({
9146
- table: "addon_admin_admin",
9147
- where: { email: devEmail },
9148
- data: devData
9149
- });
9150
- } else {
9151
- await ctx.db.insData({
9152
- table: "addon_admin_admin",
9153
- data: devData
9154
- });
9155
- }
9156
9169
  }
9157
9170
 
9158
9171
  // sync/syncMenu.ts
@@ -10081,8 +10094,14 @@ async function getTableColumnsRuntime(runtime, tableName) {
10081
10094
  let max = null;
10082
10095
  const m = /^(\w+)\s*\((\d+)\)/.exec(baseType);
10083
10096
  if (m) {
10084
- baseType = m[1];
10085
- max = Number(m[2]);
10097
+ const base = m[1];
10098
+ const maxText = m[2];
10099
+ if (typeof base === "string") {
10100
+ baseType = base;
10101
+ }
10102
+ if (typeof maxText === "string") {
10103
+ max = Number(maxText);
10104
+ }
10086
10105
  }
10087
10106
  columns[row.name] = {
10088
10107
  type: baseType.toLowerCase(),
@@ -10111,9 +10130,13 @@ async function getTableIndexesRuntime(runtime, tableName) {
10111
10130
  const q = getSyncTableIndexesQuery({ dialect: "mysql", table: tableName, dbName: runtime.dbName });
10112
10131
  const result = await runtime.db.unsafe(q.sql, q.params);
10113
10132
  for (const row of result.data) {
10114
- if (!indexes[row.INDEX_NAME])
10115
- indexes[row.INDEX_NAME] = [];
10116
- indexes[row.INDEX_NAME].push(row.COLUMN_NAME);
10133
+ const indexName = row.INDEX_NAME;
10134
+ const current = indexes[indexName];
10135
+ if (Array.isArray(current)) {
10136
+ current.push(row.COLUMN_NAME);
10137
+ } else {
10138
+ indexes[indexName] = [row.COLUMN_NAME];
10139
+ }
10117
10140
  }
10118
10141
  } else if (runtime.dbDialect === "postgresql") {
10119
10142
  const q = getSyncTableIndexesQuery({ dialect: "postgresql", table: tableName, dbName: runtime.dbName });
@@ -10121,7 +10144,8 @@ async function getTableIndexesRuntime(runtime, tableName) {
10121
10144
  for (const row of result.data) {
10122
10145
  const m = /\(([^)]+)\)/.exec(row.indexdef);
10123
10146
  if (m) {
10124
- const col = m[1].replace(/"/g, "").trim();
10147
+ const colPart = m[1];
10148
+ const col = typeof colPart === "string" ? colPart.replace(/"/g, "").trim() : "";
10125
10149
  indexes[row.indexname] = [col];
10126
10150
  }
10127
10151
  }
@@ -10154,7 +10178,8 @@ async function ensureDbVersion(dbDialect, db) {
10154
10178
  throw new Error("\u65E0\u6CD5\u83B7\u53D6 MySQL \u7248\u672C\u4FE1\u606F");
10155
10179
  }
10156
10180
  const version = r.data[0].version;
10157
- const majorVersion = parseInt(String(version).split(".")[0], 10);
10181
+ const majorPart = String(version).split(".")[0] || "0";
10182
+ const majorVersion = parseInt(majorPart, 10);
10158
10183
  if (!Number.isFinite(majorVersion) || majorVersion < DB_VERSION_REQUIREMENTS.MYSQL_MIN_MAJOR) {
10159
10184
  throw new Error(`\u6B64\u811A\u672C\u4EC5\u652F\u6301 MySQL ${DB_VERSION_REQUIREMENTS.MYSQL_MIN_MAJOR}.0+\uFF0C\u5F53\u524D\u7248\u672C: ${version}`);
10160
10185
  }
@@ -10167,7 +10192,8 @@ async function ensureDbVersion(dbDialect, db) {
10167
10192
  }
10168
10193
  const versionText = r.data[0].version;
10169
10194
  const m = /PostgreSQL\s+(\d+)/i.exec(versionText);
10170
- const major = m ? parseInt(m[1], 10) : NaN;
10195
+ const majorText = m ? m[1] : undefined;
10196
+ const major = typeof majorText === "string" ? parseInt(majorText, 10) : NaN;
10171
10197
  if (!Number.isFinite(major) || major < DB_VERSION_REQUIREMENTS.POSTGRES_MIN_MAJOR) {
10172
10198
  throw new Error(`\u6B64\u811A\u672C\u8981\u6C42 PostgreSQL >= ${DB_VERSION_REQUIREMENTS.POSTGRES_MIN_MAJOR}\uFF0C\u5F53\u524D: ${versionText}`);
10173
10199
  }
@@ -10179,7 +10205,10 @@ async function ensureDbVersion(dbDialect, db) {
10179
10205
  throw new Error("\u65E0\u6CD5\u83B7\u53D6 SQLite \u7248\u672C\u4FE1\u606F");
10180
10206
  }
10181
10207
  const version = r.data[0].version;
10182
- const [maj, min, patch] = String(version).split(".").map((v) => parseInt(v, 10) || 0);
10208
+ const parts = String(version).split(".").map((v) => parseInt(v, 10) || 0);
10209
+ const maj = parts[0] ?? 0;
10210
+ const min = parts[1] ?? 0;
10211
+ const patch = parts[2] ?? 0;
10183
10212
  const vnum = maj * 1e4 + min * 100 + patch;
10184
10213
  if (!Number.isFinite(vnum) || vnum < DB_VERSION_REQUIREMENTS.SQLITE_MIN_VERSION_NUM) {
10185
10214
  throw new Error(`\u6B64\u811A\u672C\u8981\u6C42 SQLite >= ${DB_VERSION_REQUIREMENTS.SQLITE_MIN_VERSION}\uFF0C\u5F53\u524D: ${version}`);
@@ -10209,7 +10238,11 @@ function compareFieldDefinition(dbDialect, existingColumn, fieldDef) {
10209
10238
  }
10210
10239
  }
10211
10240
  const typeMapping = getTypeMapping(dbDialect);
10212
- const expectedType = typeMapping[fieldDef.type].toLowerCase();
10241
+ const mapped = typeMapping[fieldDef.type];
10242
+ if (typeof mapped !== "string") {
10243
+ throw new Error(`\u672A\u77E5\u5B57\u6BB5\u7C7B\u578B\u6620\u5C04\uFF1Adialect=${dbDialect} type=${String(fieldDef.type)}`);
10244
+ }
10245
+ const expectedType = mapped.toLowerCase();
10213
10246
  const currentType = existingColumn.type.toLowerCase();
10214
10247
  if (currentType !== expectedType) {
10215
10248
  changes.push({
@@ -10519,8 +10552,8 @@ var calcPerfTime = (startTime, endTime = Bun.nanoseconds()) => {
10519
10552
  // utils/processInfo.ts
10520
10553
  function getProcessRole(env) {
10521
10554
  const runtimeEnv = env || {};
10522
- const bunWorkerId = runtimeEnv.BUN_WORKER_ID;
10523
- const pm2InstanceId = runtimeEnv.PM2_INSTANCE_ID;
10555
+ const bunWorkerId = runtimeEnv["BUN_WORKER_ID"];
10556
+ const pm2InstanceId = runtimeEnv["PM2_INSTANCE_ID"];
10524
10557
  if (bunWorkerId !== undefined) {
10525
10558
  return {
10526
10559
  role: bunWorkerId === "" ? "primary" : "worker",
@@ -12307,7 +12340,7 @@ class Validator {
12307
12340
  return {
12308
12341
  code: failed ? 1 : 0,
12309
12342
  failed,
12310
- firstError: failed ? errors[0] : null,
12343
+ firstError: failed ? errors[0] ?? null : null,
12311
12344
  errors,
12312
12345
  errorFields,
12313
12346
  fieldErrors
@@ -12821,18 +12854,44 @@ class DbUtils {
12821
12854
  throw new Error("tableRef \u4E0D\u80FD\u4E3A\u7A7A");
12822
12855
  }
12823
12856
  const parts = trimmed.split(/\s+/).filter((p) => p.length > 0);
12857
+ if (parts.length === 0) {
12858
+ throw new Error("tableRef \u4E0D\u80FD\u4E3A\u7A7A");
12859
+ }
12824
12860
  if (parts.length > 2) {
12825
12861
  throw new Error(`\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08\u5305\u542B\u8FC7\u591A\u7247\u6BB5\uFF09\u3002\u8BF7\u4F7F\u7528\u6700\u7B80\u5F62\u5F0F\uFF1Atable \u6216 table alias \u6216 schema.table \u6216 schema.table alias (tableRef: ${trimmed})`);
12826
12862
  }
12827
12863
  const namePart = parts[0];
12828
- const aliasPart = parts.length === 2 ? parts[1] : null;
12864
+ if (typeof namePart !== "string" || namePart.trim() === "") {
12865
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1A\u7F3A\u5C11\u8868\u540D (tableRef: ${trimmed})`);
12866
+ }
12867
+ let aliasPart = null;
12868
+ if (parts.length === 2) {
12869
+ const alias = parts[1];
12870
+ if (typeof alias !== "string" || alias.trim() === "") {
12871
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1A\u7F3A\u5C11 alias (tableRef: ${trimmed})`);
12872
+ }
12873
+ aliasPart = alias;
12874
+ }
12829
12875
  const nameSegments = namePart.split(".");
12830
12876
  if (nameSegments.length > 2) {
12831
12877
  throw new Error(`\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08schema \u5C42\u7EA7\u8FC7\u6DF1\uFF09 (tableRef: ${trimmed})`);
12832
12878
  }
12833
- const schema = nameSegments.length === 2 ? nameSegments[0] : null;
12834
- const table = nameSegments.length === 2 ? nameSegments[1] : nameSegments[0];
12835
- return { schema, table, alias: aliasPart };
12879
+ if (nameSegments.length === 2) {
12880
+ const schema = nameSegments[0];
12881
+ const table2 = nameSegments[1];
12882
+ if (typeof schema !== "string" || schema.trim() === "") {
12883
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Aschema \u4E3A\u7A7A (tableRef: ${trimmed})`);
12884
+ }
12885
+ if (typeof table2 !== "string" || table2.trim() === "") {
12886
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Atable \u4E3A\u7A7A (tableRef: ${trimmed})`);
12887
+ }
12888
+ return { schema, table: table2, alias: aliasPart };
12889
+ }
12890
+ const table = nameSegments[0];
12891
+ if (typeof table !== "string" || table.trim() === "") {
12892
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Atable \u4E3A\u7A7A (tableRef: ${trimmed})`);
12893
+ }
12894
+ return { schema: null, table, alias: aliasPart };
12836
12895
  }
12837
12896
  static normalizeTableRef(tableRef) {
12838
12897
  const parsed = DbUtils.parseTableRef(tableRef);
@@ -12910,7 +12969,15 @@ class DbUtils {
12910
12969
  if (typeof item !== "string" || !item.includes("#")) {
12911
12970
  return item;
12912
12971
  }
12913
- const [field, direction] = item.split("#");
12972
+ const parts = item.split("#");
12973
+ if (parts.length !== 2) {
12974
+ return item;
12975
+ }
12976
+ const field = parts[0];
12977
+ const direction = parts[1];
12978
+ if (typeof field !== "string" || typeof direction !== "string") {
12979
+ return item;
12980
+ }
12914
12981
  return `${snakeCase(field.trim())}#${direction.trim()}`;
12915
12982
  });
12916
12983
  }
@@ -12920,14 +12987,26 @@ class DbUtils {
12920
12987
  }
12921
12988
  if (field.toUpperCase().includes(" AS ")) {
12922
12989
  const parts = field.split(/\s+AS\s+/i);
12923
- const fieldPart = parts[0].trim();
12924
- const aliasPart = parts[1].trim();
12925
- return `${DbUtils.processJoinField(fieldPart)} AS ${aliasPart}`;
12990
+ const fieldPart = parts[0];
12991
+ const aliasPart = parts[1];
12992
+ if (typeof fieldPart !== "string" || typeof aliasPart !== "string") {
12993
+ return field;
12994
+ }
12995
+ return `${DbUtils.processJoinField(fieldPart.trim())} AS ${aliasPart.trim()}`;
12926
12996
  }
12927
12997
  if (field.includes(".")) {
12928
12998
  const parts = field.split(".");
12929
- const tableName = parts[0];
12930
- const fieldName = parts[1];
12999
+ if (parts.length < 2) {
13000
+ return snakeCase(field);
13001
+ }
13002
+ if (parts.some((p) => p.trim() === "")) {
13003
+ return field;
13004
+ }
13005
+ const fieldName = parts[parts.length - 1];
13006
+ const tableName = parts.slice(0, parts.length - 1).join(".");
13007
+ if (typeof fieldName !== "string" || typeof tableName !== "string") {
13008
+ return snakeCase(field);
13009
+ }
12931
13010
  return `${tableName.trim()}.${snakeCase(fieldName)}`;
12932
13011
  }
12933
13012
  return snakeCase(field);
@@ -12942,16 +13021,34 @@ class DbUtils {
12942
13021
  const operator = key.substring(lastDollarIndex);
12943
13022
  if (fieldPart.includes(".")) {
12944
13023
  const parts = fieldPart.split(".");
12945
- const tableName = parts[0];
12946
- const fieldName = parts[1];
13024
+ if (parts.length < 2) {
13025
+ return `${snakeCase(fieldPart)}${operator}`;
13026
+ }
13027
+ if (parts.some((p) => p.trim() === "")) {
13028
+ return `${snakeCase(fieldPart)}${operator}`;
13029
+ }
13030
+ const fieldName = parts[parts.length - 1];
13031
+ const tableName = parts.slice(0, parts.length - 1).join(".");
13032
+ if (typeof fieldName !== "string" || typeof tableName !== "string") {
13033
+ return `${snakeCase(fieldPart)}${operator}`;
13034
+ }
12947
13035
  return `${tableName.trim()}.${snakeCase(fieldName)}${operator}`;
12948
13036
  }
12949
13037
  return `${snakeCase(fieldPart)}${operator}`;
12950
13038
  }
12951
13039
  if (key.includes(".")) {
12952
13040
  const parts = key.split(".");
12953
- const tableName = parts[0];
12954
- const fieldName = parts[1];
13041
+ if (parts.length < 2) {
13042
+ return snakeCase(key);
13043
+ }
13044
+ if (parts.some((p) => p.trim() === "")) {
13045
+ return snakeCase(key);
13046
+ }
13047
+ const fieldName = parts[parts.length - 1];
13048
+ const tableName = parts.slice(0, parts.length - 1).join(".");
13049
+ if (typeof fieldName !== "string" || typeof tableName !== "string") {
13050
+ return snakeCase(key);
13051
+ }
12955
13052
  return `${tableName.trim()}.${snakeCase(fieldName)}`;
12956
13053
  }
12957
13054
  return snakeCase(key);
@@ -12984,7 +13081,15 @@ class DbUtils {
12984
13081
  if (typeof item !== "string" || !item.includes("#")) {
12985
13082
  return item;
12986
13083
  }
12987
- const [field, direction] = item.split("#");
13084
+ const parts = item.split("#");
13085
+ if (parts.length !== 2) {
13086
+ return item;
13087
+ }
13088
+ const field = parts[0];
13089
+ const direction = parts[1];
13090
+ if (typeof field !== "string" || typeof direction !== "string") {
13091
+ return item;
13092
+ }
12988
13093
  return `${DbUtils.processJoinField(field.trim())}#${direction.trim()}`;
12989
13094
  });
12990
13095
  }
@@ -13105,10 +13210,10 @@ class DbUtils {
13105
13210
  for (const [key, value] of Object.entries(userData)) {
13106
13211
  result[key] = value;
13107
13212
  }
13108
- result.id = options.id;
13109
- result.created_at = options.now;
13110
- result.updated_at = options.now;
13111
- result.state = 1;
13213
+ result["id"] = options.id;
13214
+ result["created_at"] = options.now;
13215
+ result["updated_at"] = options.now;
13216
+ result["state"] = 1;
13112
13217
  return result;
13113
13218
  }
13114
13219
  static buildUpdateRow(options) {
@@ -13118,7 +13223,7 @@ class DbUtils {
13118
13223
  for (const [key, value] of Object.entries(userData)) {
13119
13224
  result[key] = value;
13120
13225
  }
13121
- result.updated_at = options.now;
13226
+ result["updated_at"] = options.now;
13122
13227
  return result;
13123
13228
  }
13124
13229
  static buildPartialUpdateData(options) {
@@ -13242,6 +13347,7 @@ var SqlBuilderError = {
13242
13347
  QUOTE_IDENT_NEED_STRING: (identifier) => `quoteIdent \u9700\u8981\u5B57\u7B26\u4E32\u7C7B\u578B\u6807\u8BC6\u7B26 (identifier: ${String(identifier)})`,
13243
13348
  IDENT_EMPTY: "SQL \u6807\u8BC6\u7B26\u4E0D\u80FD\u4E3A\u7A7A",
13244
13349
  FIELD_EXPR_NOT_ALLOWED: (field) => `\u5B57\u6BB5\u5305\u542B\u51FD\u6570/\u8868\u8FBE\u5F0F\uFF0C\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${field})`,
13350
+ FIELD_INVALID: (field) => `\u5B57\u6BB5\u683C\u5F0F\u975E\u6CD5\uFF0C\u8BF7\u4F7F\u7528\u7B80\u5355\u5B57\u6BB5\u540D\u6216\u5B89\u5168\u5F15\u7528\uFF0C\u590D\u6742\u8868\u8FBE\u5F0F\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${field})`,
13245
13351
  FROM_EMPTY: "FROM \u8868\u540D\u4E0D\u80FD\u4E3A\u7A7A",
13246
13352
  FROM_NEED_NON_EMPTY: (table) => `FROM \u8868\u540D\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32 (table: ${String(table)})`,
13247
13353
  FROM_REQUIRED: "FROM \u8868\u540D\u662F\u5FC5\u9700\u7684",
@@ -13345,10 +13451,18 @@ class SqlBuilder {
13345
13451
  }
13346
13452
  if (field.toUpperCase().includes(" AS ")) {
13347
13453
  const parts = field.split(/\s+AS\s+/i);
13348
- const fieldPart = parts[0].trim();
13349
- const aliasPart = parts[1].trim();
13350
- SqlCheck.assertSafeAlias(aliasPart);
13351
- return `${this._escapeField(fieldPart)} AS ${aliasPart}`;
13454
+ if (parts.length !== 2) {
13455
+ throw new Error(SqlBuilderError.FIELD_INVALID(field));
13456
+ }
13457
+ const fieldPart = parts[0];
13458
+ const aliasPart = parts[1];
13459
+ if (typeof fieldPart !== "string" || typeof aliasPart !== "string") {
13460
+ throw new Error(SqlBuilderError.FIELD_INVALID(field));
13461
+ }
13462
+ const cleanFieldPart = fieldPart.trim();
13463
+ const cleanAliasPart = aliasPart.trim();
13464
+ SqlCheck.assertSafeAlias(cleanAliasPart);
13465
+ return `${this._escapeField(cleanFieldPart)} AS ${cleanAliasPart}`;
13352
13466
  }
13353
13467
  if (field.includes(".")) {
13354
13468
  const parts = field.split(".");
@@ -13382,16 +13496,26 @@ class SqlBuilder {
13382
13496
  if (parts.length > 2) {
13383
13497
  throw new Error(SqlBuilderError.TABLE_REF_TOO_MANY_PARTS(table));
13384
13498
  }
13385
- const namePart = parts[0].trim();
13386
- const aliasPart = parts.length === 2 ? parts[1].trim() : null;
13499
+ const namePartRaw = parts[0];
13500
+ if (typeof namePartRaw !== "string" || namePartRaw.trim() === "") {
13501
+ throw new Error(SqlBuilderError.FROM_EMPTY);
13502
+ }
13503
+ const namePart = namePartRaw.trim();
13504
+ const aliasPartRaw = parts.length === 2 ? parts[1] : null;
13505
+ const aliasPart = typeof aliasPartRaw === "string" ? aliasPartRaw.trim() : null;
13387
13506
  const nameSegments = namePart.split(".");
13388
13507
  if (nameSegments.length > 2) {
13389
13508
  throw new Error(SqlBuilderError.TABLE_REF_SCHEMA_TOO_DEEP(table));
13390
13509
  }
13391
13510
  let escapedName = "";
13392
13511
  if (nameSegments.length === 2) {
13393
- const schema = nameSegments[0].trim();
13394
- const tableName = nameSegments[1].trim();
13512
+ const schemaRaw = nameSegments[0];
13513
+ const tableNameRaw = nameSegments[1];
13514
+ if (typeof schemaRaw !== "string" || typeof tableNameRaw !== "string") {
13515
+ throw new Error(SqlBuilderError.TABLE_REF_SCHEMA_TOO_DEEP(table));
13516
+ }
13517
+ const schema = schemaRaw.trim();
13518
+ const tableName = tableNameRaw.trim();
13395
13519
  const escapedSchema = this._isQuotedIdent(schema) ? schema : (() => {
13396
13520
  if (this._startsWithQuote(schema) && !this._isQuotedIdent(schema)) {
13397
13521
  throw new Error(SqlBuilderError.SCHEMA_QUOTE_NOT_PAIRED(schema));
@@ -13408,7 +13532,11 @@ class SqlBuilder {
13408
13532
  })();
13409
13533
  escapedName = `${escapedSchema}.${escapedTableName}`;
13410
13534
  } else {
13411
- const tableName = nameSegments[0].trim();
13535
+ const tableNameRaw = nameSegments[0];
13536
+ if (typeof tableNameRaw !== "string") {
13537
+ throw new Error(SqlBuilderError.FROM_EMPTY);
13538
+ }
13539
+ const tableName = tableNameRaw.trim();
13412
13540
  if (this._isQuotedIdent(tableName)) {
13413
13541
  escapedName = tableName;
13414
13542
  } else {
@@ -13666,7 +13794,15 @@ class SqlBuilder {
13666
13794
  if (typeof item !== "string" || !item.includes("#")) {
13667
13795
  throw new Error(SqlBuilderError.ORDER_BY_ITEM_NEED_HASH(item));
13668
13796
  }
13669
- const [fieldName, direction] = item.split("#");
13797
+ const parts = item.split("#");
13798
+ if (parts.length !== 2) {
13799
+ throw new Error(SqlBuilderError.ORDER_BY_ITEM_NEED_HASH(item));
13800
+ }
13801
+ const fieldName = parts[0];
13802
+ const direction = parts[1];
13803
+ if (typeof fieldName !== "string" || typeof direction !== "string") {
13804
+ throw new Error(SqlBuilderError.ORDER_BY_ITEM_NEED_HASH(item));
13805
+ }
13670
13806
  const cleanField = fieldName.trim();
13671
13807
  const cleanDir = direction.trim().toUpperCase();
13672
13808
  if (!cleanField) {
@@ -13763,7 +13899,9 @@ class SqlBuilder {
13763
13899
  for (let i = 0;i < data.length; i++) {
13764
13900
  const row = data[i];
13765
13901
  for (const field of fields) {
13766
- params.push(row[field]);
13902
+ const value = row[field];
13903
+ this._validateParam(value);
13904
+ params.push(value);
13767
13905
  }
13768
13906
  }
13769
13907
  return { sql, params };
@@ -13778,7 +13916,12 @@ class SqlBuilder {
13778
13916
  const escapedFields = fields.map((field) => this._escapeField(field));
13779
13917
  const placeholders = fields.map(() => "?").join(", ");
13780
13918
  const sql = `INSERT INTO ${escapedTable} (${escapedFields.join(", ")}) VALUES (${placeholders})`;
13781
- const params = fields.map((field) => data[field]);
13919
+ const params = [];
13920
+ for (const field of fields) {
13921
+ const value = data[field];
13922
+ this._validateParam(value);
13923
+ params.push(value);
13924
+ }
13782
13925
  return { sql, params };
13783
13926
  }
13784
13927
  }
@@ -13880,7 +14023,9 @@ class SqlBuilder {
13880
14023
  }
13881
14024
  whenList.push("WHEN ? THEN ?");
13882
14025
  args.push(row.id);
13883
- args.push(row.data[field]);
14026
+ const value = row.data[field];
14027
+ SqlCheck.assertNoUndefinedParam(value, "SQL \u53C2\u6570\u503C");
14028
+ args.push(value);
13884
14029
  }
13885
14030
  if (whenList.length === 0) {
13886
14031
  continue;
@@ -13912,7 +14057,7 @@ class DbHelper {
13912
14057
  constructor(options) {
13913
14058
  this.redis = options.redis;
13914
14059
  this.sql = options.sql || null;
13915
- this.isTransaction = !!options.sql;
14060
+ this.isTransaction = Boolean(options.sql);
13916
14061
  this.dialect = options.dialect ? options.dialect : new MySqlDialect;
13917
14062
  }
13918
14063
  createSqlBuilder() {
@@ -14042,7 +14187,8 @@ class DbHelper {
14042
14187
  }
14043
14188
  async getCount(options) {
14044
14189
  const { table, where, joins, tableQualifier } = await this.prepareQueryOptions(options);
14045
- const builder = this.createSqlBuilder().selectRaw("COUNT(*) as count").from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, !!joins));
14190
+ const hasJoins = Array.isArray(joins) && joins.length > 0;
14191
+ const builder = this.createSqlBuilder().selectRaw("COUNT(*) as count").from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, hasJoins));
14046
14192
  this.applyJoins(builder, joins);
14047
14193
  const { sql, params } = builder.toSelectSql();
14048
14194
  const execRes = await this.executeWithConn(sql, params);
@@ -14054,7 +14200,8 @@ class DbHelper {
14054
14200
  }
14055
14201
  async getOne(options) {
14056
14202
  const { table, fields, where, joins, tableQualifier } = await this.prepareQueryOptions(options);
14057
- const builder = this.createSqlBuilder().select(fields).from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, !!joins));
14203
+ const hasJoins = Array.isArray(joins) && joins.length > 0;
14204
+ const builder = this.createSqlBuilder().select(fields).from(table).where(DbUtils.addDefaultStateFilter(where, tableQualifier, hasJoins));
14058
14205
  this.applyJoins(builder, joins);
14059
14206
  const { sql, params } = builder.toSelectSql();
14060
14207
  const execRes = await this.executeWithConn(sql, params);
@@ -14074,12 +14221,16 @@ class DbHelper {
14074
14221
  sql: execRes.sql
14075
14222
  };
14076
14223
  }
14077
- const data = convertBigIntFields([deserialized])[0];
14224
+ const convertedList = convertBigIntFields([deserialized]);
14225
+ const data = convertedList[0] ?? deserialized;
14078
14226
  return {
14079
14227
  data,
14080
14228
  sql: execRes.sql
14081
14229
  };
14082
14230
  }
14231
+ async getDetail(options) {
14232
+ return await this.getOne(options);
14233
+ }
14083
14234
  async getList(options) {
14084
14235
  const prepared = await this.prepareQueryOptions(options);
14085
14236
  if (prepared.page < 1 || prepared.page > 1e4) {
@@ -14088,7 +14239,7 @@ class DbHelper {
14088
14239
  if (prepared.limit < 1 || prepared.limit > 1000) {
14089
14240
  throw new Error(`\u6BCF\u9875\u6570\u91CF\u5FC5\u987B\u5728 1 \u5230 1000 \u4E4B\u95F4 (table: ${options.table}, page: ${prepared.page}, limit: ${prepared.limit})`);
14090
14241
  }
14091
- const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, !!prepared.joins);
14242
+ const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, Array.isArray(prepared.joins) && prepared.joins.length > 0);
14092
14243
  const countBuilder = this.createSqlBuilder().selectRaw("COUNT(*) as total").from(prepared.table).where(whereFiltered);
14093
14244
  this.applyJoins(countBuilder, prepared.joins);
14094
14245
  const { sql: countSql, params: countParams } = countBuilder.toSelectSql();
@@ -14136,8 +14287,21 @@ class DbHelper {
14136
14287
  async getAll(options) {
14137
14288
  const MAX_LIMIT = 1e4;
14138
14289
  const WARNING_LIMIT = 1000;
14139
- const prepared = await this.prepareQueryOptions({ ...options, page: 1, limit: 10 });
14140
- const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, !!prepared.joins);
14290
+ const prepareOptions = {
14291
+ table: options.table,
14292
+ page: 1,
14293
+ limit: 10
14294
+ };
14295
+ if (options.fields !== undefined)
14296
+ prepareOptions.fields = options.fields;
14297
+ if (options.where !== undefined)
14298
+ prepareOptions.where = options.where;
14299
+ if (options.joins !== undefined)
14300
+ prepareOptions.joins = options.joins;
14301
+ if (options.orderBy !== undefined)
14302
+ prepareOptions.orderBy = options.orderBy;
14303
+ const prepared = await this.prepareQueryOptions(prepareOptions);
14304
+ const whereFiltered = DbUtils.addDefaultStateFilter(prepared.where, prepared.tableQualifier, Array.isArray(prepared.joins) && prepared.joins.length > 0);
14141
14305
  const countBuilder = this.createSqlBuilder().selectRaw("COUNT(*) as total").from(prepared.table).where(whereFiltered);
14142
14306
  this.applyJoins(countBuilder, prepared.joins);
14143
14307
  const { sql: countSql, params: countParams } = countBuilder.toSelectSql();
@@ -14197,7 +14361,7 @@ class DbHelper {
14197
14361
  const builder = this.createSqlBuilder();
14198
14362
  const { sql, params } = builder.toInsertSql(snakeTable, processed);
14199
14363
  const execRes = await this.executeWithConn(sql, params);
14200
- const insertedId = processed.id || execRes.data?.lastInsertRowid || 0;
14364
+ const insertedId = processed["id"] || execRes.data?.lastInsertRowid || 0;
14201
14365
  return {
14202
14366
  data: insertedId,
14203
14367
  sql: execRes.sql
@@ -14222,7 +14386,11 @@ class DbHelper {
14222
14386
  }
14223
14387
  const now = Date.now();
14224
14388
  const processedList = dataList.map((data, index) => {
14225
- return DbUtils.buildInsertRow({ data, id: ids[index], now });
14389
+ const id = ids[index];
14390
+ if (typeof id !== "number") {
14391
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u751F\u6210 ID \u5931\u8D25\uFF1Aids[${index}] \u4E0D\u662F number (table: ${snakeTable})`);
14392
+ }
14393
+ return DbUtils.buildInsertRow({ data, id, now });
14226
14394
  });
14227
14395
  const insertFields = SqlCheck.assertBatchInsertRowsConsistent(processedList, { table: snakeTable });
14228
14396
  const builder = this.createSqlBuilder();
@@ -14505,38 +14673,50 @@ class Jwt {
14505
14673
  };
14506
14674
  }
14507
14675
  sign(payload, options = {}) {
14508
- const key = options.secret || this.config.secret || "befly-secret";
14676
+ const key = options.secret || this.config.secret;
14509
14677
  const algorithm = options.algorithm || this.config.algorithm || "HS256";
14510
- const signer = import_fast_jwt.createSigner({
14678
+ const signerOptions = {
14511
14679
  key,
14512
14680
  algorithm,
14513
- expiresIn: options.expiresIn || this.config.expiresIn,
14514
- iss: options.issuer,
14515
- aud: options.audience,
14516
- sub: options.subject,
14517
- jti: options.jwtId,
14518
- notBefore: typeof options.notBefore === "number" ? options.notBefore : undefined
14519
- });
14681
+ expiresIn: options.expiresIn ?? this.config.expiresIn
14682
+ };
14683
+ if (options.issuer !== undefined)
14684
+ signerOptions.iss = options.issuer;
14685
+ if (options.audience !== undefined)
14686
+ signerOptions.aud = options.audience;
14687
+ if (options.subject !== undefined)
14688
+ signerOptions.sub = options.subject;
14689
+ if (options.jwtId !== undefined)
14690
+ signerOptions.jti = options.jwtId;
14691
+ if (typeof options.notBefore === "number")
14692
+ signerOptions.notBefore = options.notBefore;
14693
+ const signer = import_fast_jwt.createSigner(signerOptions);
14520
14694
  return signer(payload);
14521
14695
  }
14522
14696
  verify(token, options = {}) {
14523
14697
  if (!token || typeof token !== "string") {
14524
14698
  throw new Error("Token\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
14525
14699
  }
14526
- const key = options.secret || this.config.secret || "befly-secret";
14700
+ const key = options.secret || this.config.secret;
14527
14701
  const algorithm = this.config.algorithm || "HS256";
14528
14702
  const algorithms = options.algorithms ? options.algorithms : [algorithm];
14529
- const verifier = import_fast_jwt.createVerifier({
14703
+ const verifierOptions = {
14530
14704
  key,
14531
14705
  algorithms,
14532
- allowedIss: options.issuer,
14533
- allowedAud: options.audience,
14534
- allowedSub: options.subject,
14535
- ignoreExpiration: options.ignoreExpiration,
14536
- ignoreNotBefore: options.ignoreNotBefore,
14537
14706
  cache: true,
14538
14707
  cacheTTL: 600000
14539
- });
14708
+ };
14709
+ if (options.issuer !== undefined)
14710
+ verifierOptions.allowedIss = options.issuer;
14711
+ if (options.audience !== undefined)
14712
+ verifierOptions.allowedAud = options.audience;
14713
+ if (options.subject !== undefined)
14714
+ verifierOptions.allowedSub = options.subject;
14715
+ if (typeof options.ignoreExpiration === "boolean")
14716
+ verifierOptions.ignoreExpiration = options.ignoreExpiration;
14717
+ if (typeof options.ignoreNotBefore === "boolean")
14718
+ verifierOptions.ignoreNotBefore = options.ignoreNotBefore;
14719
+ const verifier = import_fast_jwt.createVerifier(verifierOptions);
14540
14720
  return verifier(token);
14541
14721
  }
14542
14722
  decode(token, complete = false) {
@@ -15147,7 +15327,7 @@ async function scanFiles(dir, source, type, pattern) {
15147
15327
  const base = {
15148
15328
  source,
15149
15329
  type,
15150
- sourceName: { core: "\u6838\u5FC3", addon: "\u7EC4\u4EF6", app: "\u9879\u76EE" }[source],
15330
+ sourceName: source === "core" ? "\u6838\u5FC3" : source === "addon" ? "\u7EC4\u4EF6" : "\u9879\u76EE",
15151
15331
  filePath: normalizedFile,
15152
15332
  relativePath,
15153
15333
  fileName,
@@ -15158,28 +15338,28 @@ async function scanFiles(dir, source, type, pattern) {
15158
15338
  customKeys: isPlainObject(content) ? Object.keys(content) : []
15159
15339
  };
15160
15340
  if (type === "table") {
15161
- base.content = content;
15341
+ base["content"] = content;
15162
15342
  results.push(base);
15163
15343
  continue;
15164
15344
  }
15165
15345
  if (type === "api") {
15166
- base.auth = true;
15167
- base.rawBody = false;
15168
- base.method = "POST";
15169
- base.fields = {};
15170
- base.required = [];
15346
+ base["auth"] = true;
15347
+ base["rawBody"] = false;
15348
+ base["method"] = "POST";
15349
+ base["fields"] = {};
15350
+ base["required"] = [];
15171
15351
  }
15172
15352
  if (type === "plugin" || type === "hook") {
15173
- base.deps = [];
15174
- base.name = "";
15175
- base.handler = null;
15353
+ base["deps"] = [];
15354
+ base["name"] = "";
15355
+ base["handler"] = null;
15176
15356
  }
15177
15357
  forOwn(content, (value, key) => {
15178
15358
  base[key] = value;
15179
15359
  });
15180
15360
  if (type === "api") {
15181
- base.routePrefix = source === "app" ? "/app" : `/addon/${addonName}`;
15182
- base.routePath = `/api${base.routePrefix}/${relativePath}`;
15361
+ base["routePrefix"] = source === "app" ? "/app" : `/addon/${addonName}`;
15362
+ base["path"] = `/api${base["routePrefix"]}/${relativePath}`;
15183
15363
  }
15184
15364
  results.push(base);
15185
15365
  }
@@ -15235,11 +15415,11 @@ class Befly {
15235
15415
  async start(env = {}) {
15236
15416
  try {
15237
15417
  const serverStartTime = Bun.nanoseconds();
15238
- this.config = await loadBeflyConfig(env.NODE_ENV || "development");
15418
+ this.config = await loadBeflyConfig(env["NODE_ENV"] || "development");
15239
15419
  this.context.config = this.config;
15240
15420
  this.context.env = env;
15241
15421
  const { apis, tables, plugins, hooks, addons } = await scanSources();
15242
- this.context.addons = addons;
15422
+ this.context["addons"] = addons;
15243
15423
  await checkApi(apis);
15244
15424
  await checkTable(tables);
15245
15425
  await checkPlugin(plugins);
@@ -15262,15 +15442,23 @@ class Befly {
15262
15442
  await syncTable(this.context, tables);
15263
15443
  await syncApi(this.context, apis);
15264
15444
  await syncMenu(this.context, checkedMenus);
15265
- await syncDev(this.context, { devEmail: this.config.devEmail, devPassword: this.config.devPassword });
15445
+ const devEmail = this.config.devEmail;
15446
+ const devPassword = this.config.devPassword;
15447
+ if (typeof devEmail === "string" && devEmail.length > 0 && typeof devPassword === "string" && devPassword.length > 0) {
15448
+ await syncDev(this.context, { devEmail, devPassword });
15449
+ } else {
15450
+ Logger.debug("\u8DF3\u8FC7 syncDev\uFF1A\u672A\u914D\u7F6E devEmail/devPassword");
15451
+ }
15266
15452
  await syncCache(this.context);
15267
15453
  this.hooks = await loadHooks(hooks);
15268
15454
  this.apis = await loadApis(apis);
15269
15455
  const apiFetch = apiHandler(this.apis, this.hooks, this.context);
15270
15456
  const staticFetch = staticHandler(this.config.cors);
15457
+ const port = typeof this.config.appPort === "number" ? this.config.appPort : 3000;
15458
+ const hostname = typeof this.config.appHost === "string" && this.config.appHost.length > 0 ? this.config.appHost : "0.0.0.0";
15271
15459
  const server = Bun.serve({
15272
- port: this.config.appPort,
15273
- hostname: this.config.appHost,
15460
+ port,
15461
+ hostname,
15274
15462
  development: this.config.nodeEnv === "development",
15275
15463
  idleTimeout: 30,
15276
15464
  fetch: async (req, bunServer) => {