fix: prevent relay type normalization to http on PATCH

Bug: updateProxyRegistrySchema inherited .default("http") from the base
schema, causing PATCH to silently overwrite relay types (vercel/deno/cloudflare)
with "http" when the client didn't send a type field.

- Move .default('http') from proxyRegistryFieldsSchema to
  createProxyRegistrySchema (only applies to new proxies)
- Strip undefined keys from validated changes before passing to
  updateProxy — .partial() leaves absent fields as undefined, which
  the spread merge in updateProxyRow would propagate to the DB
- Add console.warn in extractRelayAuth when decrypt fails on a
  known-encrypted relayAuthEnc blob

Closes #6905
This commit is contained in:
oyi77
2026-07-12 02:13:42 +07:00
parent 1b6b44b172
commit a8c8417741
3 changed files with 25 additions and 4 deletions

View File

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

View File

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

View File

@@ -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();