11import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema" ;
2- import { PenBoxIcon , PlusIcon } from "lucide-react" ;
2+ import { PenBoxIcon , PlusIcon , Trash2 } from "lucide-react" ;
33import { useEffect , useState } from "react" ;
4- import { useForm } from "react-hook-form" ;
4+ import { useFieldArray , useForm } from "react-hook-form" ;
55import { toast } from "sonner" ;
66import { z } from "zod" ;
77import { AlertBlock } from "@/components/shared/alert-block" ;
@@ -35,6 +35,10 @@ import {
3535} from "@/components/ui/select" ;
3636import { cn } from "@/lib/utils" ;
3737import { api } from "@/utils/api" ;
38+ import {
39+ ADDITIONAL_FLAG_ERROR ,
40+ ADDITIONAL_FLAG_REGEX ,
41+ } from "@dokploy/server/db/validations/destination" ;
3842import { S3_PROVIDERS } from "./constants" ;
3943
4044const addDestination = z . object ( {
@@ -46,6 +50,16 @@ const addDestination = z.object({
4650 region : z . string ( ) ,
4751 endpoint : z . string ( ) . min ( 1 , "Endpoint is required" ) ,
4852 serverId : z . string ( ) . optional ( ) ,
53+ additionalFlags : z
54+ . array (
55+ z . object ( {
56+ value : z
57+ . string ( )
58+ . min ( 1 , "Flag cannot be empty" )
59+ . regex ( ADDITIONAL_FLAG_REGEX , ADDITIONAL_FLAG_ERROR ) ,
60+ } ) ,
61+ )
62+ . optional ( ) ,
4963} ) ;
5064
5165type AddDestination = z . infer < typeof addDestination > ;
@@ -89,9 +103,16 @@ export const HandleDestinations = ({ destinationId }: Props) => {
89103 region : "" ,
90104 secretAccessKey : "" ,
91105 endpoint : "" ,
106+ additionalFlags : [ ] ,
92107 } ,
93108 resolver : zodResolver ( addDestination ) ,
94109 } ) ;
110+
111+ const { fields, append, remove } = useFieldArray ( {
112+ control : form . control ,
113+ name : "additionalFlags" ,
114+ } ) ;
115+
95116 useEffect ( ( ) => {
96117 if ( destination ) {
97118 form . reset ( {
@@ -102,6 +123,8 @@ export const HandleDestinations = ({ destinationId }: Props) => {
102123 bucket : destination . bucket ,
103124 region : destination . region ,
104125 endpoint : destination . endpoint ,
126+ additionalFlags :
127+ destination . additionalFlags ?. map ( ( f ) => ( { value : f } ) ) ?? [ ] ,
105128 } ) ;
106129 } else {
107130 form . reset ( ) ;
@@ -118,6 +141,7 @@ export const HandleDestinations = ({ destinationId }: Props) => {
118141 region : data . region ,
119142 secretAccessKey : data . secretAccessKey ,
120143 destinationId : destinationId || "" ,
144+ additionalFlags : data . additionalFlags ?. map ( ( f ) => f . value ) ?? [ ] ,
121145 } )
122146 . then ( async ( ) => {
123147 toast . success ( `Destination ${ destinationId ? "Updated" : "Created" } ` ) ;
@@ -127,9 +151,12 @@ export const HandleDestinations = ({ destinationId }: Props) => {
127151 }
128152 setOpen ( false ) ;
129153 } )
130- . catch ( ( ) => {
154+ . catch ( ( e ) => {
131155 toast . error (
132156 `Error ${ destinationId ? "Updating" : "Creating" } the Destination` ,
157+ {
158+ description : e . message ,
159+ } ,
133160 ) ;
134161 } ) ;
135162 } ;
@@ -141,6 +168,7 @@ export const HandleDestinations = ({ destinationId }: Props) => {
141168 "secretAccessKey" ,
142169 "bucket" ,
143170 "endpoint" ,
171+ "additionalFlags" ,
144172 ] ) ;
145173
146174 if ( ! result ) {
@@ -179,6 +207,8 @@ export const HandleDestinations = ({ destinationId }: Props) => {
179207 region,
180208 secretAccessKey : secretKey ,
181209 serverId,
210+ additionalFlags :
211+ form . getValues ( "additionalFlags" ) ?. map ( ( f ) => f . value ) ?? [ ] ,
182212 } )
183213 . then ( ( ) => {
184214 toast . success ( "Connection Success" ) ;
@@ -358,6 +388,48 @@ export const HandleDestinations = ({ destinationId }: Props) => {
358388 </ FormItem >
359389 ) }
360390 />
391+ < div className = "flex flex-col gap-2" >
392+ < div className = "flex items-center justify-between" >
393+ < FormLabel > Additional Flags (Optional)</ FormLabel >
394+ < Button
395+ type = "button"
396+ variant = "ghost"
397+ size = "sm"
398+ onClick = { ( ) => append ( { value : "" } ) }
399+ >
400+ < PlusIcon className = "size-4" />
401+ Add Flag
402+ </ Button >
403+ </ div >
404+ { fields . map ( ( field , index ) => (
405+ < FormField
406+ key = { field . id }
407+ control = { form . control }
408+ name = { `additionalFlags.${ index } .value` }
409+ render = { ( { field } ) => (
410+ < FormItem >
411+ < div className = "flex items-center gap-2" >
412+ < FormControl >
413+ < Input
414+ placeholder = "--s3-sign-accept-encoding=false"
415+ { ...field }
416+ />
417+ </ FormControl >
418+ < Button
419+ type = "button"
420+ variant = "ghost"
421+ size = "icon"
422+ onClick = { ( ) => remove ( index ) }
423+ >
424+ < Trash2 className = "size-4 text-muted-foreground" />
425+ </ Button >
426+ </ div >
427+ < FormMessage />
428+ </ FormItem >
429+ ) }
430+ />
431+ ) ) }
432+ </ div >
361433 </ form >
362434
363435 < DialogFooter
0 commit comments