@@ -45,7 +45,34 @@ const AddRegistrySchema = z.object({
4545 password : z . string ( ) . min ( 1 , {
4646 message : "Password is required" ,
4747 } ) ,
48- registryUrl : z . string ( ) ,
48+ registryUrl : z
49+ . string ( )
50+ . optional ( )
51+ . refine (
52+ ( val ) => {
53+ // If empty or undefined, skip validation (field is optional)
54+ if ( ! val || val . trim ( ) . length === 0 ) {
55+ return true ;
56+ }
57+ // Validate that it's a valid hostname (no protocol, no path, optional port)
58+ // Valid formats: example.com, registry.example.com, [::1], example.com:5000
59+ // Invalid: https://example.com, example.com/path
60+ const trimmed = val . trim ( ) ;
61+ // Check for protocol or path - these are not allowed
62+ if ( / ^ h t t p s ? : \/ \/ / i. test ( trimmed ) || trimmed . includes ( "/" ) ) {
63+ return false ;
64+ }
65+ // Basic hostname validation: allow alphanumeric, dots, hyphens, underscores, and IPv6 in brackets
66+ // Allow optional port at the end
67+ const hostnameRegex =
68+ / ^ (?: \[ [ ^ \] ] + \] | [ a - z A - Z 0 - 9 ] (?: [ a - z A - Z 0 - 9 . _ - ] { 0 , 253 } [ a - z A - Z 0 - 9 ] ) ? ) (?: : \d + ) ? $ / ;
69+ return hostnameRegex . test ( trimmed ) ;
70+ } ,
71+ {
72+ message :
73+ "Invalid registry URL. Please enter only the hostname (e.g., example.com or registry.example.com). Do not include protocol (https://) or paths." ,
74+ } ,
75+ ) ,
4976 imagePrefix : z . string ( ) ,
5077 serverId : z . string ( ) . optional ( ) ,
5178} ) ;
@@ -99,6 +126,9 @@ export const HandleRegistry = ({ registryId }: Props) => {
99126 const registryName = form . watch ( "registryName" ) ;
100127 const imagePrefix = form . watch ( "imagePrefix" ) ;
101128 const serverId = form . watch ( "serverId" ) ;
129+ const selectedServer = servers ?. find (
130+ ( server ) => server . serverId === serverId ,
131+ ) ;
102132
103133 useEffect ( ( ) => {
104134 if ( registry ) {
@@ -125,7 +155,7 @@ export const HandleRegistry = ({ registryId }: Props) => {
125155 password : data . password ,
126156 registryName : data . registryName ,
127157 username : data . username ,
128- registryUrl : data . registryUrl ,
158+ registryUrl : data . registryUrl || "" ,
129159 registryType : "cloud" ,
130160 imagePrefix : data . imagePrefix ,
131161 serverId : data . serverId ,
@@ -261,6 +291,10 @@ export const HandleRegistry = ({ registryId }: Props) => {
261291 render = { ( { field } ) => (
262292 < FormItem >
263293 < FormLabel > Registry URL</ FormLabel >
294+ < FormDescription >
295+ Enter only the hostname (e.g.,
296+ aws_account_id.dkr.ecr.us-west-2.amazonaws.com).
297+ </ FormDescription >
264298 < FormControl >
265299 < Input
266300 placeholder = "aws_account_id.dkr.ecr.us-west-2.amazonaws.com"
@@ -282,8 +316,40 @@ export const HandleRegistry = ({ registryId }: Props) => {
282316 < FormItem >
283317 < FormLabel > Server { ! isCloud && "(Optional)" } </ FormLabel >
284318 < FormDescription >
285- Select a server to test the registry. this will run the
286- following command on the server
319+ { ! isCloud ? (
320+ < >
321+ { serverId && serverId !== "none" && selectedServer ? (
322+ < >
323+ Authentication will be performed on{ " " }
324+ < strong > { selectedServer . name } </ strong > . This
325+ registry will be available on this server.
326+ </ >
327+ ) : (
328+ < >
329+ Choose where to authenticate with the registry. By
330+ default, authentication occurs on the Dokploy
331+ server. Select a specific server to authenticate
332+ from that server instead.
333+ </ >
334+ ) }
335+ </ >
336+ ) : (
337+ < >
338+ { serverId && serverId !== "none" && selectedServer ? (
339+ < >
340+ Authentication will be performed on{ " " }
341+ < strong > { selectedServer . name } </ strong > . This
342+ registry will be available on this server.
343+ </ >
344+ ) : (
345+ < >
346+ Select a server to authenticate with the registry.
347+ The authentication will be performed from the
348+ selected server.
349+ </ >
350+ ) }
351+ </ >
352+ ) }
287353 </ FormDescription >
288354 < FormControl >
289355 < Select
@@ -345,7 +411,7 @@ export const HandleRegistry = ({ registryId }: Props) => {
345411 await testRegistry ( {
346412 username : username ,
347413 password : password ,
348- registryUrl : registryUrl ,
414+ registryUrl : registryUrl || "" ,
349415 registryName : registryName ,
350416 registryType : "cloud" ,
351417 imagePrefix : imagePrefix ,
0 commit comments