Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { api } from "@/utils/api";

const DockerProviderSchema = z.object({
dockerImage: z.string().min(1, {
message: "Docker image is required",
}),
registryId: z.string().optional(),
username: z.string().optional(),
password: z.string().optional(),
registryURL: z.string().optional(),
Expand All @@ -32,22 +42,27 @@ interface Props {

export const SaveDockerProvider = ({ applicationId }: Props) => {
const { data, refetch } = api.application.one.useQuery({ applicationId });
const { data: registries } = api.registry.all.useQuery();

const { mutateAsync } = api.application.saveDockerProvider.useMutation();
const form = useForm<DockerProvider>({
defaultValues: {
dockerImage: "",
registryId: "none",
password: "",
username: "",
registryURL: "",
},
resolver: zodResolver(DockerProviderSchema),
});

const watchRegistryId = form.watch("registryId");

useEffect(() => {
if (data) {
form.reset({
dockerImage: data.dockerImage || "",
registryId: data.registryId || "none",
password: data.password || "",
username: data.username || "",
registryURL: data.registryUrl || "",
Expand All @@ -56,12 +71,18 @@ export const SaveDockerProvider = ({ applicationId }: Props) => {
}, [form.reset, data?.applicationId, form]);

const onSubmit = async (values: DockerProvider) => {
const registryId =
values.registryId && values.registryId !== "none"
? values.registryId
: null;

await mutateAsync({
dockerImage: values.dockerImage,
password: values.password || null,
applicationId,
username: values.username || null,
registryUrl: values.registryURL || null,
registryId,
password: registryId ? null : values.password || null,
username: registryId ? null : values.username || null,
registryUrl: registryId ? null : values.registryURL || null,
})
.then(async () => {
toast.success("Docker Provider Saved");
Expand Down Expand Up @@ -96,56 +117,93 @@ export const SaveDockerProvider = ({ applicationId }: Props) => {
</div>
<FormField
control={form.control}
name="registryURL"
name="registryId"
render={({ field }) => (
<FormItem>
<FormLabel>Registry URL</FormLabel>
<FormControl>
<Input placeholder="Registry URL" {...field} />
</FormControl>
<FormMessage />
<FormLabel>Registry</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<SelectTrigger>
<SelectValue placeholder="Select a registry" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="none">None</SelectItem>
{registries?.map((registry) => (
<SelectItem
key={registry.registryId}
value={registry.registryId}
>
{registry.registryName}
</SelectItem>
))}
<SelectLabel>
Registries ({registries?.length || 0})
</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
</FormItem>
)}
/>
<div className="space-y-4">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
placeholder="Username"
autoComplete="username"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="space-y-4">
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Password"
autoComplete="one-time-code"
{...field}
type="password"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{(!watchRegistryId || watchRegistryId === "none") && (
<>
<FormField
control={form.control}
name="registryURL"
render={({ field }) => (
<FormItem>
<FormLabel>Registry URL</FormLabel>
<FormControl>
<Input placeholder="Registry URL" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="space-y-4">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
placeholder="Username"
autoComplete="username"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="space-y-4">
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Password"
autoComplete="one-time-code"
{...field}
type="password"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</>
)}
</div>

<div className="flex flex-row justify-end">
Expand Down
16 changes: 13 additions & 3 deletions apps/dokploy/server/api/routers/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,11 +535,21 @@ export const applicationRouter = createTRPCRouter({
}
await updateApplication(input.applicationId, {
dockerImage: input.dockerImage,
username: input.username,
password: input.password,
sourceType: "docker",
applicationStatus: "idle",
registryUrl: input.registryUrl,
...(input.registryId
? {
registryId: input.registryId,
username: null,
password: null,
registryUrl: null,
}
: {
registryId: null,
username: input.username,
password: input.password,
registryUrl: input.registryUrl,
}),
});

return true;
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/db/schema/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ export const apiSaveDockerProvider = createSchema
username: true,
password: true,
registryUrl: true,
registryId: true,
})
.required();

Expand Down
7 changes: 7 additions & 0 deletions packages/server/src/utils/builders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ export const getAuthConfig = (application: ApplicationNested) => {
} = application;

if (sourceType === "docker") {
if (registry) {
return {
password: registry.password,
username: registry.username,
serveraddress: registry.registryUrl,
};
}
if (username && password) {
return {
password,
Expand Down
5 changes: 3 additions & 2 deletions packages/server/src/utils/cluster/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ export const getRegistryTag = (registry: Registry, imageName: string) => {
const targetPrefix = imagePrefix || username;
const finalRegistry = registryUrl || "";

return finalRegistry
const tag = finalRegistry
? `${finalRegistry}/${targetPrefix}/${repositoryName}`
: `${targetPrefix}/${repositoryName}`;
return tag.toLowerCase();
};

const getRegistryCommands = (
Expand All @@ -122,7 +123,7 @@ echo "${registry.password}" | docker login ${registry.registryUrl} -u '${registr
exit 1;
}
echo "✅ Registry Login Success" ;
docker tag ${imageName} ${registryTag} || {
docker tag ${imageName.toLowerCase()} ${registryTag} || {
echo "❌ Error tagging image" ;
exit 1;
}
Expand Down
16 changes: 11 additions & 5 deletions packages/server/src/utils/providers/docker.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import type { ApplicationNested } from "../builders";

export const buildRemoteDocker = async (application: ApplicationNested) => {
const { registryUrl, dockerImage, username, password } = application;
const { registry, dockerImage, username, password, registryUrl } =
application;

const loginUsername = registry?.username || username;
const loginPassword = registry?.password || password;
const loginRegistryUrl = registry?.registryUrl || registryUrl;

try {
if (!dockerImage) {
throw new Error("Docker image not found");
}

let command = `
echo "Pulling ${dockerImage}";
echo "Pulling ${dockerImage}";
`;

if (username && password) {
if (loginUsername && loginPassword) {
command += `
if ! echo "${password}" | docker login --username "${username}" --password-stdin "${registryUrl || ""}" 2>&1; then
if ! echo "${loginPassword}" | docker login --username "${loginUsername}" --password-stdin "${loginRegistryUrl || ""}" 2>&1; then
echo "❌ Login failed";
exit 1;
fi
`;
}

command += `
docker pull ${dockerImage} 2>&1 || {
docker pull ${dockerImage} 2>&1 || {
echo "❌ Pulling image failed";
exit 1;
}
Expand Down