fix(mount): clearer WebDAV 400 diagnosis and path normalize

Probe / first, encode CJK/space path segments, explain 400 with path and /dav hint.
This commit is contained in:
noelorin 2026-09-27 16:18:12 +08:00
parent 6bced13135
commit fb3747a66c
1 changed files with 28 additions and 6 deletions

View File

@ -152,10 +152,21 @@ export const mountService = {
username: input.username, username: input.username,
password: input.password, password: input.password,
}); });
await listDirectory(client, input.rootPath || "/"); // 1) 先探 WebDAV 根:区分「认证/不是 WebDAV」与「根路径写错」
try {
await listDirectory(client, "/");
} catch (e) {
const msg = describeWebdavError(e);
if (msg.includes("认证失败") || msg.includes("不是 WebDAV") || msg.includes("连接")) {
return { ok: false, message: `${msg}(探测 WebDAV 根路径 / 时失败)` };
}
// 根 400 等:仍继续试 rootPath,部分服务根不可列
}
const root = normalizeWebdavPath(input.rootPath || "/");
await listDirectory(client, root);
return { ok: true, message: "连接成功" }; return { ok: true, message: "连接成功" };
} catch (e) { } catch (e) {
return { ok: false, message: describeWebdavError(e) }; return { ok: false, message: describeWebdavError(e, input.rootPath || "/") };
} }
}, },
@ -165,7 +176,7 @@ export const mountService = {
): Promise<{ entries: DirEntry[]; path: string }> { ): Promise<{ entries: DirEntry[]; path: string }> {
const mount = await mountDao.getByIdForUser(input.mountId, userId); const mount = await mountDao.getByIdForUser(input.mountId, userId);
if (!mount) throw new TRPCError({ code: "NOT_FOUND", message: "挂载不存在" }); if (!mount) throw new TRPCError({ code: "NOT_FOUND", message: "挂载不存在" });
const abs = joinWebdavPath(mount.rootPath, input.path); const abs = normalizeWebdavPath(joinWebdavPath(mount.rootPath, input.path));
try { try {
const entries = await withClient(mount, (c) => listDirectory(c, abs)); const entries = await withClient(mount, (c) => listDirectory(c, abs));
entries.sort((a, b) => { entries.sort((a, b) => {
@ -183,16 +194,17 @@ export const mountService = {
}; };
/** 把 webdav/网络底层错误归一化成用户可读的失败原因 */ /** 把 webdav/网络底层错误归一化成用户可读的失败原因 */
function describeWebdavError(e: unknown): string { function describeWebdavError(e: unknown, path?: string): string {
const raw = e instanceof Error ? e.message : String(e); const raw = e instanceof Error ? e.message : String(e);
const s = raw.toLowerCase(); const s = raw.toLowerCase();
const pathHint = path ? `(路径 ${path})` : "";
if ( if (
s.includes("401") || s.includes("401") ||
s.includes("403") || s.includes("403") ||
s.includes("unauthorized") || s.includes("unauthorized") ||
s.includes("forbidden") s.includes("forbidden")
) )
return "认证失败,请检查用户名和密码"; return `认证失败,请检查用户名和密码${pathHint}`;
if (s.includes("enotfound") || s.includes("eai_again") || s.includes("getaddrinfo")) if (s.includes("enotfound") || s.includes("eai_again") || s.includes("getaddrinfo"))
return "无法解析服务器地址"; return "无法解析服务器地址";
if (s.includes("econnrefused")) return "连接被拒绝,请检查地址和端口"; if (s.includes("econnrefused")) return "连接被拒绝,请检查地址和端口";
@ -200,8 +212,18 @@ function describeWebdavError(e: unknown): string {
return "连接超时"; return "连接超时";
if (s.includes("certificate") || s.includes("ssl") || s.includes("tls")) if (s.includes("certificate") || s.includes("ssl") || s.includes("tls"))
return "HTTPS 证书错误"; return "HTTPS 证书错误";
if (s.includes("400") || s.includes("bad request"))
return `HTTP 400:服务器拒绝该请求${pathHint}。请确认 WebDAV 地址含正确前缀(如 http://主机:端口/dav),根路径为共享目录且 URL 可用`;
if (s.includes("404") || s.includes("405") || s.includes("not found")) if (s.includes("404") || s.includes("405") || s.includes("not found"))
return "地址不可用(可能不是 WebDAV 服务)"; return `地址或路径不存在${pathHint}(可能不是 WebDAV 服务,或根路径不正确)`;
const short = raw.length > 120 ? `${raw.slice(0, 120)}…` : raw; const short = raw.length > 120 ? `${raw.slice(0, 120)}…` : raw;
return `连接失败:${short}`; return `连接失败:${short}`;
} }
/** WebDAV 路径:补前导 /、去重斜杠、段级百分号编码(中文/空格) */
function normalizeWebdavPath(input: string): string {
const trimmed = input.trim() || "/";
const parts = trimmed.split("/").filter(Boolean);
const encoded = parts.map((seg) => encodeURIComponent(decodeURIComponent(seg)));
return `/${encoded.join("/")}`;
}