Skip to content

Commit 46491ff

Browse files
committed
fix: google分片上传
feat(api): 用语义化的预览URL/下载URL替换原始URL,用于分享和文件系统 refactor(fs): 用集中式视图状态机替换预览状态标志 feat(storage): 修复管理配置中的布尔字段显示并刷新 GitHub releases 挂载增加GitHub releases存储驱动 - 调整 GitHub 仓库挂载逻辑以避免额外的目录层 - 仅对同源或代理视频链接启用 CORS 模式 - 移除对页面和 API 调用的激进 service-worker 缓存 - 控制平面现在返回独立的内联(预览)和附件(下载)URL - 文本/代码/markdown/html预览使用新的/api/share/content/:slug和/api/fs/content端点 - 管理员/用户文件详情API支持include=links参数以获取预览/下载URL - 前端组合式函数和组件已更新以使用预览URL/下载URL字段 - 文档已更新以反映新的端点路径和响应结构
1 parent 0b80fc1 commit 46491ff

File tree

3 files changed

+62
-50
lines changed

3 files changed

+62
-50
lines changed

backend/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ app.use("*", async (c, next) => {
138138
"If-Modified-Since",
139139
"If-Unmodified-Since",
140140
"Lock-Token",
141+
"Content-Range",
141142
"Content-Length",
142143
"X-Requested-With",
143144
// FS / Share 流式上传自定义头

backend/src/routes/fs/multipart.js

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,33 @@ import { getEncryptionSecret } from "../../utils/environmentUtils.js";
88
import { usePolicy } from "../../security/policies/policies.js";
99
import { findUploadSessionById } from "../../utils/uploadSessions.js";
1010

11+
const toAbsoluteUrlIfRelative = (requestUrl, maybeUrl) => {
12+
if (typeof maybeUrl !== "string" || maybeUrl.length === 0) {
13+
return maybeUrl;
14+
}
15+
if (!maybeUrl.startsWith("/")) {
16+
return maybeUrl;
17+
}
18+
const origin = new URL(requestUrl).origin;
19+
return new URL(maybeUrl, origin).toString();
20+
};
21+
22+
const ensureAbsoluteSessionUploadUrl = (c, payload) => {
23+
const session = payload?.session;
24+
const uploadUrl = session?.uploadUrl;
25+
const absolute = toAbsoluteUrlIfRelative(c.req.url, uploadUrl);
26+
if (!session || absolute === uploadUrl) {
27+
return payload;
28+
}
29+
return {
30+
...payload,
31+
session: {
32+
...session,
33+
uploadUrl: absolute,
34+
},
35+
};
36+
};
37+
1138
const parseJsonBody = async (c, next) => {
1239
const body = await c.req.json();
1340
c.set("jsonBody", body);
@@ -64,9 +91,17 @@ export const registerMultipartRoutes = (router, helpers) => {
6491

6592
const mountManager = new MountManager(db, encryptionSecret, repositoryFactory);
6693
const fileSystem = new FileSystem(mountManager);
67-
const result = await fileSystem.initializeFrontendMultipartUpload(path, fileName, fileSize, userIdOrInfo, userType, partSize, partCount);
94+
const result = await fileSystem.initializeFrontendMultipartUpload(
95+
path,
96+
fileName,
97+
fileSize,
98+
userIdOrInfo,
99+
userType,
100+
partSize,
101+
partCount,
102+
);
68103

69-
return jsonOk(c, result, "前端分片上传初始化成功");
104+
return jsonOk(c, ensureAbsoluteSessionUploadUrl(c, result), "前端分片上传初始化成功");
70105
});
71106

72107
router.post("/api/fs/multipart/complete", parseJsonBody, usePolicy("fs.upload", { pathResolver: jsonPathResolver() }), async (c) => {
@@ -142,7 +177,7 @@ export const registerMultipartRoutes = (router, helpers) => {
142177
const fileSystem = new FileSystem(mountManager);
143178
const result = await fileSystem.refreshMultipartUrls(path, uploadId, partNumbers, userIdOrInfo, userType);
144179

145-
return jsonOk(c, result, "刷新分片上传预签名URL成功");
180+
return jsonOk(c, ensureAbsoluteSessionUploadUrl(c, result), "刷新分片上传预签名URL成功");
146181
});
147182

148183
// 前端分片上传中转端点(single_session 场景)

backend/src/storage/fs/features/ops.js

Lines changed: 23 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,16 @@ export async function renameItem(fs, oldPath, newPath, userIdOrInfo, userType) {
4444
}
4545

4646
export async function copyItem(fs, sourcePath, targetPath, userIdOrInfo, userType, options = {}) {
47-
// 目录判断:用于决定是否走目录级 orchestrator
48-
const sourceIsDirectory = isDirectoryPath(sourcePath);
49-
50-
const targetIsDirectory = isDirectoryPath(targetPath);
51-
let effectiveTargetPath = targetPath;
52-
if (!sourceIsDirectory && targetIsDirectory) {
53-
const sourceSegments = sourcePath.split("/").filter(Boolean);
54-
const sourceFileName = sourceSegments[sourceSegments.length - 1] || "file";
55-
effectiveTargetPath = `${normalizePath(targetPath, true)}${sourceFileName}`;
56-
}
57-
5847
// 先解析源与目标挂载与驱动,在 FS 层统一做跨存储决策
5948
const sourceCtx = await fs.mountManager.getDriverByPath(sourcePath, userIdOrInfo, userType);
60-
const targetCtx = await fs.mountManager.getDriverByPath(effectiveTargetPath, userIdOrInfo, userType);
49+
const targetCtx = await fs.mountManager.getDriverByPath(targetPath, userIdOrInfo, userType);
6150

6251
const { driver: sourceDriver, mount: sourceMount, subPath: sourceSubPath } = sourceCtx;
6352
const { driver: targetDriver, mount: targetMount, subPath: targetSubPath } = targetCtx;
6453

54+
// 目录判断:用于决定是否走目录级 orchestrator
55+
const sourceIsDirectory = isDirectoryPath(sourcePath);
56+
6557
const sameMount = sourceMount.id === targetMount.id;
6658

6759
// 统一目录自复制防护:同一挂载内,禁止将目录复制到自身或其子目录
@@ -97,16 +89,13 @@ export async function copyItem(fs, sourcePath, targetPath, userIdOrInfo, userTyp
9789
skipped: true,
9890
reason: "target_exists",
9991
source: sourcePath,
100-
target: effectiveTargetPath,
92+
target: targetPath,
10193
contentLength: 0,
10294
};
10395
}
10496
} catch (checkError) {
10597
// exists 检查失败时继续复制(降级处理)
106-
console.warn(
107-
`[copyItem] skipExisting 检查失败 for ${effectiveTargetPath}:`,
108-
checkError?.message || checkError,
109-
);
98+
console.warn(`[copyItem] skipExisting 检查失败 for ${targetPath}:`, checkError?.message || checkError);
11099
}
111100
// 标记已检查,下游无需重复检查
112101
options = { ...options, _skipExistingChecked: true };
@@ -122,7 +111,7 @@ export async function copyItem(fs, sourcePath, targetPath, userIdOrInfo, userTyp
122111
});
123112
}
124113

125-
const result = await sourceDriver.copyItem(sourcePath, effectiveTargetPath, {
114+
const result = await sourceDriver.copyItem(sourcePath, targetPath, {
126115
mount: sourceMount,
127116
// 保持旧字段,同时补充明确的源/目标子路径,方便驱动精确映射存储路径
128117
subPath: sourceSubPath,
@@ -136,40 +125,27 @@ export async function copyItem(fs, sourcePath, targetPath, userIdOrInfo, userTyp
136125
...options,
137126
});
138127

139-
fs.emitCacheInvalidation({
140-
mount: sourceMount,
141-
paths: [sourcePath, effectiveTargetPath],
142-
reason: "copy",
143-
});
128+
fs.emitCacheInvalidation({ mount: sourceMount, paths: [sourcePath, targetPath], reason: "copy" });
144129
return result;
145130
}
146131

147132
// 2)跨挂载:走通用 orchestrator,支持文件和目录
148133
if (sourceIsDirectory) {
149134
// 目录:使用目录级 orchestrator,递归复制目录下所有文件
150135
return await copyDirectoryBetweenDrivers(
151-
fs,
152-
sourceCtx,
153-
targetCtx,
154-
sourcePath,
155-
effectiveTargetPath,
156-
userIdOrInfo,
157-
userType,
158-
options,
159-
);
160-
}
161-
162-
// 文件:使用单文件 orchestrator
163-
return await copyBetweenDrivers(
164136
fs,
165137
sourceCtx,
166138
targetCtx,
167139
sourcePath,
168-
effectiveTargetPath,
140+
targetPath,
169141
userIdOrInfo,
170142
userType,
171143
options,
172-
);
144+
);
145+
}
146+
147+
// 文件:使用单文件 orchestrator
148+
return await copyBetweenDrivers(fs, sourceCtx, targetCtx, sourcePath, targetPath, userIdOrInfo, userType, options);
173149
}
174150

175151
export async function batchRemoveItems(fs, paths, userIdOrInfo, userType) {
@@ -321,7 +297,7 @@ async function copyBetweenDrivers(fs, sourceCtx, targetCtx, sourcePath, targetPa
321297
const targetSegments = targetPath.split("/").filter(Boolean);
322298
const sourceSegments = sourcePath.split("/").filter(Boolean);
323299
const sourceFileName =
324-
sourceSegments[sourceSegments.length - 1] || "file";
300+
sourceSegments[sourceSegments.length - 1] || "file";
325301
const targetLeaf = targetSegments[targetSegments.length - 1] || "";
326302
const targetIsDirectory = isDirectoryPath(targetPath);
327303

@@ -461,14 +437,14 @@ async function copyDirectoryBetweenDrivers(fs, sourceCtx, targetCtx, sourcePath,
461437
const fileTargetCtx = await fs.mountManager.getDriverByPath(fileTargetPath, userIdOrInfo, userType);
462438

463439
const fileResult = await copyBetweenDrivers(
464-
fs,
465-
fileSourceCtx,
466-
fileTargetCtx,
467-
fileSourcePath,
468-
fileTargetPath,
469-
userIdOrInfo,
470-
userType,
471-
options
440+
fs,
441+
fileSourceCtx,
442+
fileTargetCtx,
443+
fileSourcePath,
444+
fileTargetPath,
445+
userIdOrInfo,
446+
userType,
447+
options
472448
);
473449

474450
// 处理复制结果:成功、跳过、失败

0 commit comments

Comments
 (0)