From 8e301dbca925fd9cd39f5778bb870c1dc8e1198f Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Fri, 29 May 2026 02:22:12 +0200 Subject: [PATCH] fix(clients): preserve UUID when toggling enable from clients page The clients list returns slim rows without secrets (uuid/password/auth) or flow/security/tgId/reset/group. setEnable built its update payload straight from the slim row, sending an empty id, so the backend treated it as a new client and regenerated the UUID (and dropped the omitted fields). Hydrate the full record first and send a complete payload that changes only the enable flag. --- frontend/src/hooks/useClients.ts | 33 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/frontend/src/hooks/useClients.ts b/frontend/src/hooks/useClients.ts index 69feadbe..8374ac30 100644 --- a/frontend/src/hooks/useClients.ts +++ b/frontend/src/hooks/useClients.ts @@ -397,20 +397,31 @@ export function useClients() { const setEnable = useCallback(async (client: ClientRecord, enable: boolean) => { if (!client?.email) return null; - const payload = { - email: client.email, - subId: client.subId, - id: client.uuid, - password: client.password, - auth: client.auth, - totalGB: client.totalGB || 0, - expiryTime: client.expiryTime || 0, - limitIp: client.limitIp || 0, - comment: client.comment || '', + const full = await hydrate(client.email); + const base = full?.client; + if (!base) return null; + const payload: Record = { + email: base.email, + subId: base.subId, + id: base.uuid, + password: base.password, + auth: base.auth, + flow: base.flow || '', + security: base.security || 'auto', + totalGB: base.totalGB || 0, + expiryTime: base.expiryTime || 0, + limitIp: base.limitIp || 0, + tgId: Number(base.tgId) || 0, + reset: Number(base.reset) || 0, + group: base.group || '', + comment: base.comment || '', enable: !!enable, }; + if (base.reverse?.tag) { + payload.reverse = { tag: base.reverse.tag }; + } return update(client.email, payload); - }, [update]); + }, [hydrate, update]); // WS-driven in-place merges. Page wires these via useWebSocket; the bridge // covers coarse 'invalidate' and 'inbounds' events centrally.