befly 3.21.2 → 3.22.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/apis/admin/list.js +1 -1
- package/apis/admin/upd.js +2 -2
- package/apis/dict/upd.js +1 -1
- package/apis/dictType/upd.js +1 -1
- package/apis/role/all.js +1 -1
- package/apis/role/list.js +1 -1
- package/apis/role/upd.js +1 -1
- package/apis/source/imageList.js +27 -0
- package/apis/upload/file.js +105 -0
- package/checks/config.js +2 -0
- package/configs/beflyConfig.json +2 -1
- package/configs/beflyMenus.json +12 -0
- package/index.js +5 -5
- package/lib/dbHelper.js +170 -751
- package/lib/dbParse.js +1045 -0
- package/lib/dbUtil.js +52 -508
- package/lib/sqlBuilder.js +78 -294
- package/package.json +2 -2
- package/paths.js +2 -2
- package/router/static.js +2 -1
- package/sql/befly.sql +20 -0
- package/tables/file.json +81 -0
- package/utils/datetime.js +29 -32
package/apis/admin/list.js
CHANGED
package/apis/admin/upd.js
CHANGED
|
@@ -39,7 +39,7 @@ export default {
|
|
|
39
39
|
if (username && username !== admin.data.username) {
|
|
40
40
|
const existingUsername = await befly.mysql.getOne({
|
|
41
41
|
table: "beflyAdmin",
|
|
42
|
-
where: { username: username, id: { $
|
|
42
|
+
where: { username: username, id: { $not: id } }
|
|
43
43
|
});
|
|
44
44
|
if (existingUsername.data?.id) {
|
|
45
45
|
return befly.tool.No("用户名已被使用");
|
|
@@ -50,7 +50,7 @@ export default {
|
|
|
50
50
|
if (nickname && nickname !== admin.data.nickname) {
|
|
51
51
|
const existingNickname = await befly.mysql.getOne({
|
|
52
52
|
table: "beflyAdmin",
|
|
53
|
-
where: { nickname: nickname, id: { $
|
|
53
|
+
where: { nickname: nickname, id: { $not: id } }
|
|
54
54
|
});
|
|
55
55
|
if (existingNickname.data?.id) {
|
|
56
56
|
return befly.tool.No("昵称已被使用");
|
package/apis/dict/upd.js
CHANGED
package/apis/dictType/upd.js
CHANGED
package/apis/role/all.js
CHANGED
package/apis/role/list.js
CHANGED
package/apis/role/upd.js
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
name: "获取图库列表",
|
|
3
|
+
method: "POST",
|
|
4
|
+
body: "none",
|
|
5
|
+
auth: true,
|
|
6
|
+
fields: {
|
|
7
|
+
page: { name: "页码", input: "integer", min: 1, max: 9999 },
|
|
8
|
+
limit: { name: "每页数量", input: "integer", min: 1, max: 100 },
|
|
9
|
+
keyword: { name: "关键词", input: "string", min: 0, max: 100 }
|
|
10
|
+
},
|
|
11
|
+
required: [],
|
|
12
|
+
handler: async (befly, ctx) => {
|
|
13
|
+
const result = await befly.mysql.getList({
|
|
14
|
+
table: "beflyFile",
|
|
15
|
+
where: {
|
|
16
|
+
state: 1,
|
|
17
|
+
isImage: 1,
|
|
18
|
+
fileName$like: ctx.body.keyword
|
|
19
|
+
},
|
|
20
|
+
orderBy: ["id#DESC"],
|
|
21
|
+
page: ctx.body.page,
|
|
22
|
+
limit: ctx.body.limit
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return befly.tool.Yes("操作成功", result.data);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { extname, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { Logger } from "../../lib/logger.js";
|
|
5
|
+
import { getAppPublicDir } from "../../paths.js";
|
|
6
|
+
import { getMonthDir } from "../../utils/datetime.js";
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: "上传文件",
|
|
10
|
+
method: "POST",
|
|
11
|
+
body: "raw",
|
|
12
|
+
auth: true,
|
|
13
|
+
fields: {},
|
|
14
|
+
required: [],
|
|
15
|
+
handler: async (befly, ctx) => {
|
|
16
|
+
try {
|
|
17
|
+
const maxFileSizeMb = Number(befly.config?.uploadMaxSize || 20);
|
|
18
|
+
const maxFileSize = maxFileSizeMb * 1024 * 1024;
|
|
19
|
+
const requestContentType = ctx.req.headers.get("content-type") || "";
|
|
20
|
+
|
|
21
|
+
if (requestContentType.toLowerCase().startsWith("multipart/form-data") === false) {
|
|
22
|
+
return befly.tool.No("请使用 FormData 上传文件");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!ctx.userId) {
|
|
26
|
+
return befly.tool.No("用户未登录");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const formData = await ctx.req.formData();
|
|
30
|
+
const file = formData.get("file");
|
|
31
|
+
|
|
32
|
+
if (!(file instanceof File)) {
|
|
33
|
+
return befly.tool.No("缺少上传文件");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const rawBuffer = await file.arrayBuffer();
|
|
37
|
+
|
|
38
|
+
if (!rawBuffer || rawBuffer.byteLength <= 0) {
|
|
39
|
+
return befly.tool.No("上传文件内容为空");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (rawBuffer.byteLength > maxFileSize) {
|
|
43
|
+
return befly.tool.No(`文件不能超过${maxFileSizeMb}MB`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const originalFileName = file.name || "未命名文件";
|
|
47
|
+
const extension = extname(originalFileName).toLowerCase();
|
|
48
|
+
const mimeType = String(file.type || "").toLowerCase();
|
|
49
|
+
const mimeTypeGroup = mimeType.split("/")[0];
|
|
50
|
+
const fileType = ["image", "video", "audio"].includes(mimeTypeGroup) ? mimeTypeGroup : "file";
|
|
51
|
+
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const categoryDir = extension ? extension.slice(1) : "files";
|
|
54
|
+
const monthDir = getMonthDir(now, befly.config?.tz);
|
|
55
|
+
const fileKey = Bun.randomUUIDv7();
|
|
56
|
+
const savedFileName = extension ? `${fileKey}${extension}` : fileKey;
|
|
57
|
+
const uploadDir = join(getAppPublicDir(befly.config?.publicDir || "./public"), categoryDir, monthDir);
|
|
58
|
+
const relativeFilePath = `/public/${categoryDir}/${monthDir}/${savedFileName}`;
|
|
59
|
+
const absoluteFileUrl = `${befly.config.staticHost}${relativeFilePath}`;
|
|
60
|
+
const isImage = fileType === "image" ? 1 : 0;
|
|
61
|
+
|
|
62
|
+
mkdirSync(uploadDir, { recursive: true });
|
|
63
|
+
await Bun.write(join(uploadDir, savedFileName), rawBuffer);
|
|
64
|
+
|
|
65
|
+
const insertRes = await befly.mysql.insData({
|
|
66
|
+
table: "beflyFile",
|
|
67
|
+
data: {
|
|
68
|
+
userId: ctx.userId,
|
|
69
|
+
filePath: relativeFilePath,
|
|
70
|
+
url: absoluteFileUrl,
|
|
71
|
+
isImage: isImage,
|
|
72
|
+
fileType: fileType,
|
|
73
|
+
fileSize: rawBuffer.byteLength,
|
|
74
|
+
fileKey: fileKey,
|
|
75
|
+
fileExt: extension,
|
|
76
|
+
fileName: originalFileName
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!insertRes.data) {
|
|
81
|
+
return befly.tool.No("资源记录写入失败");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return befly.tool.Yes("上传成功", {
|
|
85
|
+
id: insertRes.data,
|
|
86
|
+
userId: ctx.userId,
|
|
87
|
+
filePath: relativeFilePath,
|
|
88
|
+
fileType: fileType,
|
|
89
|
+
fileSize: rawBuffer.byteLength,
|
|
90
|
+
fileKey: fileKey,
|
|
91
|
+
fileExt: extension,
|
|
92
|
+
fileName: originalFileName,
|
|
93
|
+
isImage: isImage,
|
|
94
|
+
url: absoluteFileUrl
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
Logger.error("上传文件失败", error, {
|
|
98
|
+
apiPath: ctx.apiPath,
|
|
99
|
+
userId: ctx.userId,
|
|
100
|
+
roleType: ctx.roleType
|
|
101
|
+
});
|
|
102
|
+
return befly.tool.No("上传文件失败");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
package/checks/config.js
CHANGED
|
@@ -17,9 +17,11 @@ const configSchema = z
|
|
|
17
17
|
appName: noTrimString,
|
|
18
18
|
appPort: z.int().min(1).max(65535),
|
|
19
19
|
appHost: noTrimString,
|
|
20
|
+
staticHost: noTrimString,
|
|
20
21
|
devEmail: z.union([z.literal(""), z.email()]),
|
|
21
22
|
devPassword: z.string().min(6),
|
|
22
23
|
bodyLimit: z.int().min(1),
|
|
24
|
+
uploadMaxSize: z.int().min(1),
|
|
23
25
|
tz: z.string().refine((value) => isValidTimeZone(value), "无效的时区"),
|
|
24
26
|
publicDir: noTrimString.min(1),
|
|
25
27
|
|
package/configs/beflyConfig.json
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
"devEmail": "dev@qq.com",
|
|
7
7
|
"devPassword": "111111",
|
|
8
8
|
"bodyLimit": 1048576,
|
|
9
|
+
"uploadMaxSize": 20,
|
|
9
10
|
"tz": "Asia/Shanghai",
|
|
10
11
|
"publicDir": "./public",
|
|
11
|
-
|
|
12
|
+
"staticHost": "http://127.0.0.1:3000",
|
|
12
13
|
"logger": {
|
|
13
14
|
"debug": 1,
|
|
14
15
|
"excludeFields": ["password", "token", "secret"],
|
package/configs/beflyMenus.json
CHANGED
package/index.js
CHANGED
|
@@ -208,15 +208,15 @@ export class Befly {
|
|
|
208
208
|
fetch: async (req, server) => {
|
|
209
209
|
const url = new URL(req.url);
|
|
210
210
|
|
|
211
|
-
if (url.pathname === "/") {
|
|
212
|
-
return Response.json({ code: 0, msg: `${this.context.config.appName} 接口服务已启动` });
|
|
213
|
-
}
|
|
214
|
-
|
|
215
211
|
if (url.pathname.startsWith("/api/")) {
|
|
216
212
|
return apiFetch(req, server);
|
|
217
213
|
}
|
|
218
214
|
|
|
219
|
-
|
|
215
|
+
if (url.pathname.startsWith("/public/")) {
|
|
216
|
+
return staticFetch(req);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return Response.json({ code: 1, msg: "路径不存在" }, { status: 200 });
|
|
220
220
|
},
|
|
221
221
|
error: (error) => {
|
|
222
222
|
Logger.error("服务启动时发生错误", error);
|