diff --git a/app/web/src/components/pages/MountsBody.tsx b/app/web/src/components/pages/MountsBody.tsx index 565380d..40eb6f2 100644 --- a/app/web/src/components/pages/MountsBody.tsx +++ b/app/web/src/components/pages/MountsBody.tsx @@ -183,6 +183,8 @@ function MountsInner(props: { locale: Locale }) { username: form.username.trim() || undefined, password: form.password || undefined, rootPath: form.rootPath.trim() || "/", + // 编辑留空密码时由服务端复用已存密文 + mountId: mode === "edit" ? (editId ?? undefined) : undefined, }); } diff --git a/packages/trpc/src/router/media.router.ts b/packages/trpc/src/router/media.router.ts index 90cbbf5..44b32f5 100644 --- a/packages/trpc/src/router/media.router.ts +++ b/packages/trpc/src/router/media.router.ts @@ -28,7 +28,7 @@ export const mediaRouter = t.router({ .mutation(({ ctx, input }) => mountService.delete(ctx.userId, input.id)), mountTest: protectedProcedure .input(mountSchemas.test) - .mutation(({ input }) => mountService.test(input)), + .mutation(({ ctx, input }) => mountService.test(input, ctx.userId)), mountListDir: protectedProcedure .input(mountSchemas.listDir) .query(({ ctx, input }) => mountService.listDir(ctx.userId, input)), diff --git a/packages/trpc/src/services/mount.service.ts b/packages/trpc/src/services/mount.service.ts index 85b9fef..3095a72 100644 --- a/packages/trpc/src/services/mount.service.ts +++ b/packages/trpc/src/services/mount.service.ts @@ -116,8 +116,9 @@ export const mountService = { patch["baseUrl"] = input.baseUrl; } if (input.username !== undefined) patch["username"] = input.username; - if (input.password !== undefined) { - patch["secretEnc"] = input.password ? encryptSecret(input.password) : ""; + // 密码留空/未传 = 保持原密文,避免编辑时重复输入 + if (input.password) { + patch["secretEnc"] = encryptSecret(input.password); } if (input.rootPath !== undefined) patch["rootPath"] = input.rootPath; if (input.enabled !== undefined) patch["enabled"] = input.enabled; @@ -135,22 +136,32 @@ export const mountService = { return { success: true, removedItems: removedIds.length }; }, - async test(input: { - baseUrl: string; - username?: string | undefined; - password?: string | undefined; - rootPath: string; - }): Promise<{ ok: boolean; message: string }> { + async test( + input: { + baseUrl: string; + username?: string | undefined; + password?: string | undefined; + rootPath: string; + mountId?: string | undefined; + }, + userId?: string, + ): Promise<{ ok: boolean; message: string }> { try { assertSafeWebdavUrl(input.baseUrl); } catch (e) { return { ok: false, message: e instanceof Error ? e.message : "地址不合法" }; } + // 编辑时密码留空:复用已存密文测连,避免每次重填 + let password = input.password; + if (!password && input.mountId && userId) { + const existing = await mountDao.getByIdForUser(input.mountId, userId); + if (existing?.secretEnc) password = decryptSecret(existing.secretEnc); + } try { const client = createWebdav({ baseUrl: input.baseUrl, username: input.username, - password: input.password, + password, }); // 1) 先探 WebDAV 根:区分「认证/不是 WebDAV」与「根路径写错」 try { diff --git a/packages/types/src/media-schemas.ts b/packages/types/src/media-schemas.ts index 96979d6..28eaf6c 100644 --- a/packages/types/src/media-schemas.ts +++ b/packages/types/src/media-schemas.ts @@ -46,6 +46,8 @@ export const mountSchemas = { username: z.string().max(200).optional(), password: z.string().max(500).optional(), rootPath: z.string().max(1000).default("/"), + /** 编辑已有挂载时传入:密码留空则复用库内密文 */ + mountId: z.string().optional(), }), listDir: z.object({ mountId: id,