diff --git a/src/lib/api/proxyRegistryRouteHandlers.ts b/src/lib/api/proxyRegistryRouteHandlers.ts index dc93873956..b5b2e9bf4e 100644 --- a/src/lib/api/proxyRegistryRouteHandlers.ts +++ b/src/lib/api/proxyRegistryRouteHandlers.ts @@ -94,7 +94,13 @@ export async function handleProxyUpdate(request: Request) { }); } - const { id, assignment, ...changes } = validation.data; + const { id, assignment, ...rawChanges } = validation.data; + // Strip keys the client didn't send — .partial() resolves absent fields to + // undefined, which would silently overwrite DB values via the spread merge + // in updateProxyRow. Only include keys the client explicitly provided. + const changes = Object.fromEntries( + Object.entries(rawChanges).filter(([_, v]) => v !== undefined) + ); if (assignment) { const result = await updateProxyAndAssign(id, changes, assignment); if (!result?.proxy) { diff --git a/src/lib/db/proxies/mappers.ts b/src/lib/db/proxies/mappers.ts index c9d7cae8c7..c475ad1b64 100644 --- a/src/lib/db/proxies/mappers.ts +++ b/src/lib/db/proxies/mappers.ts @@ -1,4 +1,4 @@ -import { decrypt } from "../encryption"; +import { decrypt, looksEncrypted } from "../encryption"; import type { JsonRecord, ProxyScope, @@ -68,6 +68,15 @@ export function extractRelayAuth(notes: unknown): string | undefined { if (parsed.relayAuthEnc) { const dec = decrypt(parsed.relayAuthEnc); if (dec) return dec; + // decrypt returned null despite a present blob — warn so operators + // know the key changed or went missing. The plaintext fallback below + // may still save us (legacy rows that never migrated). + if (looksEncrypted(parsed.relayAuthEnc)) { + console.warn( + `[relay] Failed to decrypt relayAuthEnc for proxy — ` + + `STORAGE_ENCRYPTION_KEY may have changed or been unset` + ); + } } return parsed.relayAuth || undefined; } catch { diff --git a/src/shared/validation/schemas/proxy.ts b/src/shared/validation/schemas/proxy.ts index db3e009a74..ff49b75e78 100644 --- a/src/shared/validation/schemas/proxy.ts +++ b/src/shared/validation/schemas/proxy.ts @@ -108,8 +108,7 @@ export const proxyRegistryFieldsSchema = z (value) => (typeof value === "string" ? value.trim().toLowerCase() : value), z.enum(["http", "https", "socks5", "vercel", "deno", "cloudflare"]) ) - .optional() - .default("http"), + .optional(), host: z.string().trim().min(1, "host is required").max(255), port: z.coerce.number().int().min(1).max(65535), username: z.string().optional(), @@ -135,6 +134,13 @@ export const proxyRegistryFieldsSchema = z export const createProxyRegistrySchema = proxyRegistryFieldsSchema .extend({ + type: z + .preprocess( + (value) => (typeof value === "string" ? value.trim().toLowerCase() : value), + z.enum(["http", "https", "socks5", "vercel", "deno", "cloudflare"]) + ) + .optional() + .default("http"), assignment: inlineProxyAssignmentSchema.optional(), }) .strict();