From d1bc109697837dc6f404727d1a740a73bedd16b8 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Tue, 16 Dec 2025 22:07:52 -0600 Subject: [PATCH 1/2] feat(registry): enhance registry handling with optional password and new test functionality - Updated the AddRegistrySchema to make the password field optional when editing an existing registry. - Introduced a new mutation, testRegistryById, to validate registry credentials using existing data. - Improved form handling to conditionally require the password based on the editing state. - Enhanced user feedback for registry testing with clearer error messages and instructions. --- .../cluster/registry/handle-registry.tsx | 97 ++++++++++++++++--- apps/dokploy/server/api/routers/registry.ts | 62 ++++++++++++ packages/server/src/db/schema/registry.ts | 8 ++ 3 files changed, 155 insertions(+), 12 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx b/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx index f751e262e..d68e23b92 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx @@ -42,9 +42,7 @@ const AddRegistrySchema = z.object({ username: z.string().min(1, { message: "Username is required", }), - password: z.string().min(1, { - message: "Password is required", - }), + password: z.string(), registryUrl: z .string() .optional() @@ -75,6 +73,7 @@ const AddRegistrySchema = z.object({ ), imagePrefix: z.string(), serverId: z.string().optional(), + isEditing: z.boolean().optional(), }); type AddRegistry = z.infer; @@ -108,6 +107,12 @@ export const HandleRegistry = ({ registryId }: Props) => { error: testRegistryError, isError: testRegistryIsError, } = api.registry.testRegistry.useMutation(); + const { + mutateAsync: testRegistryById, + isLoading: isLoadingById, + error: testRegistryByIdError, + isError: testRegistryByIdIsError, + } = api.registry.testRegistryById.useMutation(); const form = useForm({ defaultValues: { username: "", @@ -116,8 +121,23 @@ export const HandleRegistry = ({ registryId }: Props) => { imagePrefix: "", registryName: "", serverId: "", + isEditing: !!registryId, }, - resolver: zodResolver(AddRegistrySchema), + resolver: zodResolver( + AddRegistrySchema.refine( + (data) => { + // When creating a new registry, password is required + if (!data.isEditing && (!data.password || data.password.length === 0)) { + return false; + } + return true; + }, + { + message: "Password is required", + path: ["password"], + }, + ), + ), }); const password = form.watch("password"); @@ -138,6 +158,7 @@ export const HandleRegistry = ({ registryId }: Props) => { registryUrl: registry.registryUrl, imagePrefix: registry.imagePrefix || "", registryName: registry.registryName, + isEditing: true, }); } else { form.reset({ @@ -146,13 +167,13 @@ export const HandleRegistry = ({ registryId }: Props) => { registryUrl: "", imagePrefix: "", serverId: "", + isEditing: false, }); } }, [form, form.reset, form.formState.isSubmitSuccessful, registry]); const onSubmit = async (data: AddRegistry) => { - await mutateAsync({ - password: data.password, + const payload: any = { registryName: data.registryName, username: data.username, registryUrl: data.registryUrl || "", @@ -160,7 +181,15 @@ export const HandleRegistry = ({ registryId }: Props) => { imagePrefix: data.imagePrefix, serverId: data.serverId, registryId: registryId || "", - }) + }; + + // Only include password if it's been provided (not empty) + // When editing, empty password means "keep the existing password" + if (data.password && data.password.length > 0) { + payload.password = data.password; + } + + await mutateAsync(payload) .then(async (_data) => { await utils.registry.all.invalidate(); toast.success(registryId ? "Registry updated" : "Registry added"); @@ -198,11 +227,14 @@ export const HandleRegistry = ({ registryId }: Props) => { Fill the next fields to add a external registry. - {(isError || testRegistryIsError) && ( + {(isError || testRegistryIsError || testRegistryByIdIsError) && (
- {testRegistryError?.message || error?.message || ""} + {testRegistryError?.message || + testRegistryByIdError?.message || + error?.message || + ""}
)} @@ -253,10 +285,21 @@ export const HandleRegistry = ({ registryId }: Props) => { name="password" render={({ field }) => ( - Password + + Password{registryId && " (Optional)"} + + {registryId && ( + + Leave blank to keep existing password. Enter new password to test or update it. + + )} {