diff --git a/packages/trpc/src/services/mount.service.ts b/packages/trpc/src/services/mount.service.ts index b3ac9d3..85b9fef 100644 --- a/packages/trpc/src/services/mount.service.ts +++ b/packages/trpc/src/services/mount.service.ts @@ -152,10 +152,21 @@ export const mountService = { username: input.username, 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: "连接成功" }; } 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 }> { const mount = await mountDao.getByIdForUser(input.mountId, userId); 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 { const entries = await withClient(mount, (c) => listDirectory(c, abs)); entries.sort((a, b) => { @@ -183,16 +194,17 @@ export const mountService = { }; /** 把 webdav/网络底层错误归一化成用户可读的失败原因 */ -function describeWebdavError(e: unknown): string { +function describeWebdavError(e: unknown, path?: string): string { const raw = e instanceof Error ? e.message : String(e); const s = raw.toLowerCase(); + const pathHint = path ? `(路径 ${path})` : ""; if ( s.includes("401") || s.includes("403") || s.includes("unauthorized") || s.includes("forbidden") ) - return "认证失败,请检查用户名和密码"; + return `认证失败,请检查用户名和密码${pathHint}`; if (s.includes("enotfound") || s.includes("eai_again") || s.includes("getaddrinfo")) return "无法解析服务器地址"; if (s.includes("econnrefused")) return "连接被拒绝,请检查地址和端口"; @@ -200,8 +212,18 @@ function describeWebdavError(e: unknown): string { return "连接超时"; if (s.includes("certificate") || s.includes("ssl") || s.includes("tls")) 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")) - return "地址不可用(可能不是 WebDAV 服务)"; + return `地址或路径不存在${pathHint}(可能不是 WebDAV 服务,或根路径不正确)`; const short = raw.length > 120 ? `${raw.slice(0, 120)}…` : raw; 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("/")}`; +}