diff --git a/apps/dokploy/components/dashboard/application/build/show.tsx b/apps/dokploy/components/dashboard/application/build/show.tsx index 5c6e044c5..16a452257 100644 --- a/apps/dokploy/components/dashboard/application/build/show.tsx +++ b/apps/dokploy/components/dashboard/application/build/show.tsx @@ -20,7 +20,7 @@ import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -enum BuildType { +export enum BuildType { dockerfile = "dockerfile", heroku_buildpacks = "heroku_buildpacks", paketo_buildpacks = "paketo_buildpacks", @@ -29,9 +29,18 @@ enum BuildType { railpack = "railpack", } +const buildTypeDisplayMap: Record = { + [BuildType.dockerfile]: "Dockerfile", + [BuildType.railpack]: "Railpack", + [BuildType.nixpacks]: "Nixpacks", + [BuildType.heroku_buildpacks]: "Heroku Buildpacks", + [BuildType.paketo_buildpacks]: "Paketo Buildpacks", + [BuildType.static]: "Static", +}; + const mySchema = z.discriminatedUnion("buildType", [ z.object({ - buildType: z.literal("dockerfile"), + buildType: z.literal(BuildType.dockerfile), dockerfile: z .string({ required_error: "Dockerfile path is required", @@ -42,39 +51,88 @@ const mySchema = z.discriminatedUnion("buildType", [ dockerBuildStage: z.string().nullable().default(""), }), z.object({ - buildType: z.literal("heroku_buildpacks"), + buildType: z.literal(BuildType.heroku_buildpacks), herokuVersion: z.string().nullable().default(""), }), z.object({ - buildType: z.literal("paketo_buildpacks"), + buildType: z.literal(BuildType.paketo_buildpacks), }), z.object({ - buildType: z.literal("nixpacks"), + buildType: z.literal(BuildType.nixpacks), publishDirectory: z.string().optional(), }), z.object({ - buildType: z.literal("static"), + buildType: z.literal(BuildType.static), }), z.object({ - buildType: z.literal("railpack"), + buildType: z.literal(BuildType.railpack), }), ]); type AddTemplate = z.infer; + interface Props { applicationId: string; } +interface ApplicationData { + buildType: BuildType; + dockerfile?: string | null; + dockerContextPath?: string | null; + dockerBuildStage?: string | null; + herokuVersion?: string | null; + publishDirectory?: string | null; +} + +function isValidBuildType(value: string): value is BuildType { + return Object.values(BuildType).includes(value as BuildType); +} + +const resetData = (data: ApplicationData): AddTemplate => { + switch (data.buildType) { + case BuildType.dockerfile: + return { + buildType: BuildType.dockerfile, + dockerfile: data.dockerfile || "", + dockerContextPath: data.dockerContextPath || "", + dockerBuildStage: data.dockerBuildStage || "", + }; + case BuildType.heroku_buildpacks: + return { + buildType: BuildType.heroku_buildpacks, + herokuVersion: data.herokuVersion || "", + }; + case BuildType.nixpacks: + return { + buildType: BuildType.nixpacks, + publishDirectory: data.publishDirectory || undefined, + }; + case BuildType.paketo_buildpacks: + return { + buildType: BuildType.paketo_buildpacks, + }; + case BuildType.static: + return { + buildType: BuildType.static, + }; + case BuildType.railpack: + return { + buildType: BuildType.railpack, + }; + default: + const buildType = data.buildType as BuildType; + return { + buildType, + } as AddTemplate; + } +}; + export const ShowBuildChooseForm = ({ applicationId }: Props) => { const { mutateAsync, isLoading } = api.application.saveBuildType.useMutation(); const { data, refetch } = api.application.one.useQuery( - { - applicationId, - }, - { - enabled: !!applicationId, - }, + { applicationId }, + { enabled: !!applicationId }, ); const form = useForm({ @@ -85,46 +143,36 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => { }); const buildType = form.watch("buildType"); + useEffect(() => { if (data) { - if (data.buildType === "dockerfile") { - form.reset({ - buildType: data.buildType, - ...(data.buildType && { - dockerfile: data.dockerfile || "", - dockerContextPath: data.dockerContextPath || "", - dockerBuildStage: data.dockerBuildStage || "", - }), - }); - } else if (data.buildType === "heroku_buildpacks") { - form.reset({ - buildType: data.buildType, - ...(data.buildType && { - herokuVersion: data.herokuVersion || "", - }), - }); - } else { - form.reset({ - buildType: data.buildType, - publishDirectory: data.publishDirectory || undefined, - }); - } + const typedData: ApplicationData = { + ...data, + buildType: isValidBuildType(data.buildType) + ? (data.buildType as BuildType) + : BuildType.nixpacks, // fallback + }; + + form.reset(resetData(typedData)); } - }, [form.formState.isSubmitSuccessful, form.reset, data, form]); + }, [data, form]); const onSubmit = async (data: AddTemplate) => { await mutateAsync({ applicationId, buildType: data.buildType, publishDirectory: - data.buildType === "nixpacks" ? data.publishDirectory : null, - dockerfile: data.buildType === "dockerfile" ? data.dockerfile : null, + data.buildType === BuildType.nixpacks ? data.publishDirectory : null, + dockerfile: + data.buildType === BuildType.dockerfile ? data.dockerfile : null, dockerContextPath: - data.buildType === "dockerfile" ? data.dockerContextPath : null, + data.buildType === BuildType.dockerfile ? data.dockerContextPath : null, dockerBuildStage: - data.buildType === "dockerfile" ? data.dockerBuildStage : null, + data.buildType === BuildType.dockerfile ? data.dockerBuildStage : null, herokuVersion: - data.buildType === "heroku_buildpacks" ? data.herokuVersion : null, + data.buildType === BuildType.heroku_buildpacks + ? data.herokuVersion + : null, }) .then(async () => { toast.success("Build type saved"); @@ -160,193 +208,143 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => { control={form.control} name="buildType" defaultValue={form.control._defaultValues.buildType} - render={({ field }) => { - return ( - - Build Type - - - - - - - - Dockerfile - - - - - - - - Railpack{" "} - New - - - - - - - - Nixpacks - - - - - - - - Heroku Buildpacks - - - - - - - - Paketo Buildpacks - - - - - - - Static - - - - - - ); - }} + render={({ field }) => ( + + Build Type + + + {Object.entries(buildTypeDisplayMap).map( + ([value, label]) => ( + + + + + + {label} + {value === BuildType.railpack && ( + New + )} + + + ), + )} + + + + + )} /> - {buildType === "heroku_buildpacks" && ( + {buildType === BuildType.heroku_buildpacks && ( { - return ( - - Heroku Version (Optional) - - - - - - - ); - }} + render={({ field }) => ( + + Heroku Version (Optional) + + + + + + )} /> )} - {buildType === "dockerfile" && ( + {buildType === BuildType.dockerfile && ( <> { - return ( - - Docker File - - - - - - - ); - }} - /> - - { - return ( - - Docker Context Path - - - - - - - ); - }} - /> - - { - return ( - -
- Docker Build Stage - - Allows you to target a specific stage in a - Multi-stage Dockerfile. If empty, Docker defaults to - build the last defined stage. - -
- - - -
- ); - }} - /> - - )} - - {buildType === "nixpacks" && ( - { - return ( + render={({ field }) => ( -
- Publish Directory - - Allows you to serve a single directory via NGINX after - the build phase. Useful if the final build assets - should be served as a static site. - -
+ Docker File -
- ); - }} + )} + /> + ( + + Docker Context Path + + + + + + )} + /> + ( + +
+ Docker Build Stage + + Allows you to target a specific stage in a Multi-stage + Dockerfile. If empty, Docker defaults to build the + last defined stage. + +
+ + + +
+ )} + /> + + )} + {buildType === BuildType.nixpacks && ( + ( + +
+ Publish Directory + + Allows you to serve a single directory via NGINX after + the build phase. Useful if the final build assets should + be served as a static site. + +
+ + + + +
+ )} /> )}
diff --git a/packages/server/package.json b/packages/server/package.json index 7e637cb51..1ac0c8a7e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,7 +28,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "micromatch":"4.0.8", + "micromatch": "4.0.8", "@ai-sdk/anthropic": "^1.0.6", "@ai-sdk/azure": "^1.0.15", "@ai-sdk/cohere": "^1.0.6", @@ -36,11 +36,11 @@ "@ai-sdk/mistral": "^1.0.6", "@ai-sdk/openai": "^1.0.12", "@ai-sdk/openai-compatible": "^0.0.13", - "@better-auth/utils":"0.2.3", - "@oslojs/encoding":"1.1.0", - "@oslojs/crypto":"1.0.1", - "drizzle-dbml-generator":"0.10.0", - "better-auth":"1.2.4", + "@better-auth/utils": "0.2.3", + "@oslojs/encoding": "1.1.0", + "@oslojs/crypto": "1.0.1", + "drizzle-dbml-generator": "0.10.0", + "better-auth": "1.2.4", "@faker-js/faker": "^8.4.1", "@octokit/auth-app": "^6.0.4", "@react-email/components": "^0.0.21",