fix(mount): reuse stored password when editing

Empty password on update keeps secret; test connection with mountId reuses decrypted secret.
This commit is contained in:
noelorin 2026-09-27 16:30:48 +08:00
parent fb3747a66c
commit a1483299f3
4 changed files with 25 additions and 10 deletions

View File

@ -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,
});
}

View File

@ -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)),

View File

@ -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 {

View File

@ -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,