Skip to content

Commit 9e11b80

Browse files
authored
Merge pull request #3143 from Dokploy/feat/add-args-to-advanced-command
feat: add support for command arguments in application and database s…
2 parents 559753e + adfe29e commit 9e11b80

File tree

19 files changed

+7071
-40
lines changed

19 files changed

+7071
-40
lines changed

apps/dokploy/__test__/drop/drop.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const baseApp: ApplicationNested = {
3333
buildServerId: "",
3434
buildRegistryId: "",
3535
buildRegistry: null,
36+
args: [],
3637
giteaBuildPath: "",
3738
previewRequireCollaboratorPermissions: false,
3839
giteaId: "",

apps/dokploy/__test__/traefik/traefik.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const baseApp: ApplicationNested = {
1616
buildRegistry: null,
1717
giteaBuildPath: "",
1818
giteaId: "",
19+
args: [],
1920
cleanCache: false,
2021
applicationStatus: "done",
2122
endpointSpecSwarm: null,

apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { zodResolver } from "@hookform/resolvers/zod";
2+
import { Plus, Trash2 } from "lucide-react";
23
import { useEffect } from "react";
3-
import { useForm } from "react-hook-form";
4+
import { useFieldArray, useForm } from "react-hook-form";
45
import { toast } from "sonner";
56
import { z } from "zod";
67
import { Button } from "@/components/ui/button";
@@ -28,6 +29,13 @@ interface Props {
2829

2930
const AddRedirectSchema = z.object({
3031
command: z.string(),
32+
args: z
33+
.array(
34+
z.object({
35+
value: z.string().min(1, "Argument cannot be empty"),
36+
}),
37+
)
38+
.optional(),
3139
});
3240

3341
type AddCommand = z.infer<typeof AddRedirectSchema>;
@@ -47,22 +55,36 @@ export const AddCommand = ({ applicationId }: Props) => {
4755
const form = useForm<AddCommand>({
4856
defaultValues: {
4957
command: "",
58+
args: [],
5059
},
5160
resolver: zodResolver(AddRedirectSchema),
5261
});
5362

63+
const { fields, append, remove } = useFieldArray({
64+
control: form.control,
65+
name: "args",
66+
});
67+
5468
useEffect(() => {
5569
if (data?.command) {
5670
form.reset({
5771
command: data?.command || "",
72+
args: data?.args?.map((arg) => ({ value: arg })) || [],
5873
});
5974
}
60-
}, [form, form.reset, form.formState.isSubmitSuccessful, data?.command]);
75+
}, [
76+
form,
77+
form.reset,
78+
form.formState.isSubmitSuccessful,
79+
data?.command,
80+
data?.args,
81+
]);
6182

6283
const onSubmit = async (data: AddCommand) => {
6384
await mutateAsync({
6485
applicationId,
6586
command: data?.command,
87+
args: data?.args?.map((arg) => arg.value).filter(Boolean),
6688
})
6789
.then(async () => {
6890
toast.success("Command Updated");
@@ -100,13 +122,65 @@ export const AddCommand = ({ applicationId }: Props) => {
100122
<FormItem>
101123
<FormLabel>Command</FormLabel>
102124
<FormControl>
103-
<Input placeholder="Custom command" {...field} />
125+
<Input placeholder="/bin/sh" {...field} />
104126
</FormControl>
105127

106128
<FormMessage />
107129
</FormItem>
108130
)}
109131
/>
132+
133+
<div className="space-y-2">
134+
<div className="flex items-center justify-between">
135+
<FormLabel>Arguments (Args)</FormLabel>
136+
<Button
137+
type="button"
138+
variant="outline"
139+
size="sm"
140+
onClick={() => append({ value: "" })}
141+
>
142+
<Plus className="h-4 w-4 mr-1" />
143+
Add Argument
144+
</Button>
145+
</div>
146+
147+
{fields.length === 0 && (
148+
<p className="text-sm text-muted-foreground">
149+
No arguments added yet. Click "Add Argument" to add one.
150+
</p>
151+
)}
152+
153+
{fields.map((field, index) => (
154+
<FormField
155+
key={field.id}
156+
control={form.control}
157+
name={`args.${index}.value`}
158+
render={({ field }) => (
159+
<FormItem>
160+
<div className="flex gap-2">
161+
<FormControl>
162+
<Input
163+
placeholder={
164+
index === 0 ? "-c" : "echo Hello World"
165+
}
166+
{...field}
167+
/>
168+
</FormControl>
169+
<Button
170+
type="button"
171+
variant="destructive"
172+
size="icon"
173+
onClick={() => remove(index)}
174+
>
175+
<Trash2 className="h-4 w-4" />
176+
</Button>
177+
</div>
178+
<FormMessage />
179+
</FormItem>
180+
)}
181+
/>
182+
))}
183+
</div>
110184
</div>
111185
<div className="flex justify-end">
112186
<Button isLoading={isLoading} type="submit" className="w-fit">

apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { zodResolver } from "@hookform/resolvers/zod";
2+
import { Plus, Trash2 } from "lucide-react";
23
import { useEffect } from "react";
3-
import { useForm } from "react-hook-form";
4+
import { useFieldArray, useForm } from "react-hook-form";
45
import { toast } from "sonner";
56
import { z } from "zod";
67
import { Button } from "@/components/ui/button";
@@ -20,6 +21,13 @@ import type { ServiceType } from "../../application/advanced/show-resources";
2021
const addDockerImage = z.object({
2122
dockerImage: z.string().min(1, "Docker image is required"),
2223
command: z.string(),
24+
args: z
25+
.array(
26+
z.object({
27+
value: z.string().min(1, "Argument cannot be empty"),
28+
}),
29+
)
30+
.optional(),
2331
});
2432

2533
interface Props {
@@ -61,15 +69,22 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
6169
defaultValues: {
6270
dockerImage: "",
6371
command: "",
72+
args: [],
6473
},
6574
resolver: zodResolver(addDockerImage),
6675
});
6776

77+
const { fields, append, remove } = useFieldArray({
78+
control: form.control,
79+
name: "args",
80+
});
81+
6882
useEffect(() => {
6983
if (data) {
7084
form.reset({
7185
dockerImage: data.dockerImage,
7286
command: data.command || "",
87+
args: data.args?.map((arg) => ({ value: arg })) || [],
7388
});
7489
}
7590
}, [data, form, form.reset]);
@@ -83,6 +98,7 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
8398
mariadbId: id || "",
8499
dockerImage: formData?.dockerImage,
85100
command: formData?.command,
101+
args: formData?.args?.map((arg) => arg.value).filter(Boolean),
86102
})
87103
.then(async () => {
88104
toast.success("Custom Command Updated");
@@ -128,13 +144,68 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
128144
<FormItem>
129145
<FormLabel>Command</FormLabel>
130146
<FormControl>
131-
<Input placeholder="Custom command" {...field} />
147+
<Input placeholder="/bin/sh" {...field} />
132148
</FormControl>
133149

134150
<FormMessage />
135151
</FormItem>
136152
)}
137153
/>
154+
155+
<div className="space-y-2">
156+
<div className="flex items-center justify-between">
157+
<FormLabel>Arguments (Args)</FormLabel>
158+
<Button
159+
type="button"
160+
variant="outline"
161+
size="sm"
162+
onClick={() => append({ value: "" })}
163+
>
164+
<Plus className="h-4 w-4 mr-1" />
165+
Add Argument
166+
</Button>
167+
</div>
168+
169+
{fields.length === 0 && (
170+
<p className="text-sm text-muted-foreground">
171+
No arguments added yet. Click "Add Argument" to add one.
172+
</p>
173+
)}
174+
175+
{fields.map((field, index) => (
176+
<FormField
177+
key={field.id}
178+
control={form.control}
179+
name={`args.${index}.value`}
180+
render={({ field }) => (
181+
<FormItem>
182+
<div className="flex gap-2">
183+
<FormControl>
184+
<Input
185+
placeholder={
186+
index === 0
187+
? "-c"
188+
: "redis-server --port 6379"
189+
}
190+
{...field}
191+
/>
192+
</FormControl>
193+
<Button
194+
type="button"
195+
variant="destructive"
196+
size="icon"
197+
onClick={() => remove(index)}
198+
>
199+
<Trash2 className="h-4 w-4" />
200+
</Button>
201+
</div>
202+
<FormMessage />
203+
</FormItem>
204+
)}
205+
/>
206+
))}
207+
</div>
208+
138209
<div className="flex w-full justify-end">
139210
<Button isLoading={form.formState.isSubmitting} type="submit">
140211
Save
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
ALTER TABLE "application" ADD COLUMN "args" text[];--> statement-breakpoint
2+
ALTER TABLE "mariadb" ADD COLUMN "args" text[];--> statement-breakpoint
3+
ALTER TABLE "mongo" ADD COLUMN "args" text[];--> statement-breakpoint
4+
ALTER TABLE "mysql" ADD COLUMN "args" text[];--> statement-breakpoint
5+
ALTER TABLE "postgres" ADD COLUMN "args" text[];--> statement-breakpoint
6+
ALTER TABLE "redis" ADD COLUMN "args" text[];

0 commit comments

Comments
 (0)