refactor: agroupate utilities

This commit is contained in:
Mauricio Siu
2025-02-09 02:20:28 -06:00
parent f44512a437
commit 3e2cfe6eb8
13 changed files with 5826 additions and 1308 deletions

View File

@@ -15,7 +15,8 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { validateAndFormatYAML } from "../../application/advanced/traefik/update-traefik-config"; import { validateAndFormatYAML } from "../../application/advanced/traefik/update-traefik-config";
import { RandomizeCompose } from "./randomize-compose"; import { RandomizeCompose } from "./randomize-compose";
import { RandomizeDeployable } from "./randomize-deployable"; import { RandomizeDeployable } from "./isolated-deployment";
import { ShowUtilities } from "./show-utilities";
interface Props { interface Props {
composeId: string; composeId: string;
@@ -126,8 +127,7 @@ services:
</Form> </Form>
<div className="flex justify-between flex-col lg:flex-row gap-2"> <div className="flex justify-between flex-col lg:flex-row gap-2">
<div className="w-full flex flex-col lg:flex-row gap-4 items-end"> <div className="w-full flex flex-col lg:flex-row gap-4 items-end">
<RandomizeCompose composeId={composeId} /> <ShowUtilities composeId={composeId} />
<RandomizeDeployable composeId={composeId} />
</div> </div>
<Button <Button
type="submit" type="submit"

View File

@@ -0,0 +1,191 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { CodeEditor } from "@/components/shared/code-editor";
import { Button } from "@/components/ui/button";
import {
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/form";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
interface Props {
composeId: string;
}
const schema = z.object({
isolatedDeployment: z.boolean().optional(),
});
type Schema = z.infer<typeof schema>;
export const IsolatedDeployment = ({ composeId }: Props) => {
const utils = api.useUtils();
const [compose, setCompose] = useState<string>("");
const { mutateAsync, error, isError } =
api.compose.isolatedDeployment.useMutation();
const { mutateAsync: updateCompose } = api.compose.update.useMutation();
const { data, refetch } = api.compose.one.useQuery(
{ composeId },
{ enabled: !!composeId },
);
console.log(data);
const form = useForm<Schema>({
defaultValues: {
isolatedDeployment: false,
},
resolver: zodResolver(schema),
});
useEffect(() => {
randomizeCompose();
if (data) {
form.reset({
isolatedDeployment: data?.isolatedDeployment || false,
});
}
}, [form, form.reset, form.formState.isSubmitSuccessful, data]);
const onSubmit = async (formData: Schema) => {
await updateCompose({
composeId,
isolatedDeployment: formData?.isolatedDeployment || false,
})
.then(async (data) => {
randomizeCompose();
refetch();
toast.success("Compose updated");
})
.catch(() => {
toast.error("Error updating the compose");
});
};
const randomizeCompose = async () => {
await mutateAsync({
composeId,
suffix: data?.appName || "",
})
.then(async (data) => {
await utils.project.all.invalidate();
setCompose(data);
toast.success("Compose Isolated");
})
.catch(() => {
toast.error("Error isolating the compose");
});
};
return (
<>
<DialogHeader>
<DialogTitle>Isolate Deployment</DialogTitle>
<DialogDescription>
Use this option to isolate the deployment of this compose file.
</DialogDescription>
</DialogHeader>
<div className="text-sm text-muted-foreground flex flex-col gap-2">
<span>
This feature creates an isolated environment for your deployment by
adding unique prefixes to all resources. It establishes a dedicated
network based on your compose file's name, ensuring your services run
in isolation. This prevents conflicts when running multiple instances
of the same template or services with identical names.
</span>
<div className="space-y-4">
<div>
<h4 className="font-medium mb-2">
Resources that will be isolated:
</h4>
<ul className="list-disc list-inside">
<li>Docker volumes</li>
<li>Docker networks</li>
</ul>
</div>
</div>
</div>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
id="hook-form-add-project"
className="grid w-full gap-4"
>
{isError && (
<div className="flex flex-row gap-4 rounded-lg items-center bg-red-50 p-2 dark:bg-red-950">
<AlertTriangle className="text-red-600 dark:text-red-400" />
<span className="text-sm text-red-600 dark:text-red-400">
{error?.message}
</span>
</div>
)}
<div className="flex flex-col lg:flex-col gap-4 w-full ">
<div>
<FormField
control={form.control}
name="isolatedDeployment"
render={({ field }) => (
<FormItem className="mt-4 flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Enable Randomize ({data?.appName})</FormLabel>
<FormDescription>
Enable randomize to the compose file.
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="flex flex-col lg:flex-row gap-4 w-full items-end justify-end">
<Button
form="hook-form-add-project"
type="submit"
className="lg:w-fit"
>
Save
</Button>
</div>
</div>
<div className="flex flex-col gap-4">
<Label>Preview</Label>
<pre>
<CodeEditor
value={compose || ""}
language="yaml"
readOnly
height="50rem"
/>
</pre>
</div>
</form>
</Form>
</>
);
};

View File

@@ -1,14 +1,10 @@
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
import { CodeEditor } from "@/components/shared/code-editor"; import { CodeEditor } from "@/components/shared/code-editor";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { CardTitle } from "@/components/ui/card";
import { import {
Dialog,
DialogContent,
DialogDescription, DialogDescription,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { import {
Form, Form,
@@ -20,11 +16,6 @@ import {
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
@@ -70,6 +61,7 @@ export const RandomizeCompose = ({ composeId }: Props) => {
const suffix = form.watch("suffix"); const suffix = form.watch("suffix");
useEffect(() => { useEffect(() => {
randomizeCompose();
if (data) { if (data) {
form.reset({ form.reset({
suffix: data?.suffix || "", suffix: data?.suffix || "",
@@ -110,19 +102,12 @@ export const RandomizeCompose = ({ composeId }: Props) => {
}; };
return ( return (
<Dialog open={isOpen} onOpenChange={setIsOpen}> <div className="w-full">
<DialogTrigger asChild onClick={() => randomizeCompose()}>
<Button className="max-lg:w-full" variant="outline">
<Dices className="h-4 w-4" />
Randomize Compose
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-6xl max-h-[50rem] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle>Randomize Compose (Experimental)</DialogTitle> <DialogTitle>Randomize Compose (Experimental)</DialogTitle>
<DialogDescription> <DialogDescription>
Use this in case you want to deploy the same compose file and you Use this in case you want to deploy the same compose file and you have
have conflicts with some property like volumes, networks, etc. conflicts with some property like volumes, networks, etc.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="text-sm text-muted-foreground flex flex-col gap-2"> <div className="text-sm text-muted-foreground flex flex-col gap-2">
@@ -138,9 +123,8 @@ export const RandomizeCompose = ({ composeId }: Props) => {
<li>secrets</li> <li>secrets</li>
</ul> </ul>
<AlertBlock type="info"> <AlertBlock type="info">
When you activate this option, we will include a env When you activate this option, we will include a env `COMPOSE_PREFIX`
`COMPOSE_PREFIX` variable to the compose file so you can use it in variable to the compose file so you can use it in your compose file.
your compose file.
</AlertBlock> </AlertBlock>
</div> </div>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>} {isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
@@ -165,7 +149,7 @@ export const RandomizeCompose = ({ composeId }: Props) => {
control={form.control} control={form.control}
name="suffix" name="suffix"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col justify-center max-sm:items-center w-full"> <FormItem className="flex flex-col justify-center max-sm:items-center w-full mt-4">
<FormLabel>Suffix</FormLabel> <FormLabel>Suffix</FormLabel>
<FormControl> <FormControl>
<Input <Input
@@ -229,7 +213,6 @@ export const RandomizeCompose = ({ composeId }: Props) => {
</pre> </pre>
</form> </form>
</Form> </Form>
</DialogContent> </div>
</Dialog>
); );
}; };

View File

@@ -1,195 +0,0 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { CodeEditor } from "@/components/shared/code-editor";
import { Button } from "@/components/ui/button";
import { CardTitle } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, Dices } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
interface Props {
composeId: string;
}
const schema = z.object({
deployable: z.boolean().optional(),
});
type Schema = z.infer<typeof schema>;
export const RandomizeDeployable = ({ composeId }: Props) => {
const utils = api.useUtils();
const [compose, setCompose] = useState<string>("");
const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, error, isError } =
api.compose.randomizeDeployableCompose.useMutation();
const { mutateAsync: updateCompose } = api.compose.update.useMutation();
const { data, refetch } = api.compose.one.useQuery(
{ composeId },
{ enabled: !!composeId },
);
const form = useForm<Schema>({
defaultValues: {
deployable: false,
},
resolver: zodResolver(schema),
});
useEffect(() => {
if (data) {
form.reset({
deployable: data?.deployable || false,
});
}
}, [form, form.reset, form.formState.isSubmitSuccessful, data]);
const onSubmit = async (formData: Schema) => {
await updateCompose({
composeId,
deployable: formData?.deployable || false,
})
.then(async (data) => {
randomizeCompose();
refetch();
toast.success("Compose updated");
})
.catch(() => {
toast.error("Error randomizing the compose");
});
};
const randomizeCompose = async () => {
await mutateAsync({
composeId,
suffix: data?.appName || "",
})
.then(async (data) => {
await utils.project.all.invalidate();
setCompose(data);
toast.success("Compose randomized");
})
.catch(() => {
toast.error("Error randomizing the compose");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild onClick={() => randomizeCompose()}>
<Button className="max-lg:w-full" variant="outline">
<Dices className="h-4 w-4" />
Randomize Deployable
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-6xl max-h-[50rem] overflow-y-auto">
<DialogHeader>
<DialogTitle>Randomize Deployable (Experimental)</DialogTitle>
<DialogDescription>
Use this in case you want to deploy the same compose file twice.
</DialogDescription>
</DialogHeader>
<div className="text-sm text-muted-foreground flex flex-col gap-2">
<span>
This will randomize the compose file and will add a suffix to the
property to avoid conflicts
</span>
<ul className="list-disc list-inside">
<li>volumes</li>
<li>networks</li>
</ul>
<AlertBlock type="info">
When you activate this option, we will include a env
`DOKPLOY_SUFFIX` variable to the compose file so you can use it in
your compose file, also we don't include the{" "}
<code>dokploy-network</code> to any of the services by default.
</AlertBlock>
</div>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
id="hook-form-add-project"
className="grid w-full gap-4"
>
{isError && (
<div className="flex flex-row gap-4 rounded-lg items-center bg-red-50 p-2 dark:bg-red-950">
<AlertTriangle className="text-red-600 dark:text-red-400" />
<span className="text-sm text-red-600 dark:text-red-400">
{error?.message}
</span>
</div>
)}
<div className="flex flex-col lg:flex-col gap-4 w-full ">
<div>
<FormField
control={form.control}
name="deployable"
render={({ field }) => (
<FormItem className="mt-4 flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Apply Randomize ({data?.appName})</FormLabel>
<FormDescription>
Apply randomize to the compose file.
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="flex flex-col lg:flex-row gap-4 w-full items-end justify-end">
<Button
form="hook-form-add-project"
type="submit"
className="lg:w-fit"
>
Save
</Button>
</div>
</div>
<pre>
<CodeEditor
value={compose || ""}
language="yaml"
readOnly
height="50rem"
/>
</pre>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,46 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { IsolatedDeployment } from "./isolated-deployment";
import { RandomizeCompose } from "./randomize-compose";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { useState } from "react";
import { Button } from "@/components/ui/button";
interface Props {
composeId: string;
}
export const ShowUtilities = ({ composeId }: Props) => {
const [isOpen, setIsOpen] = useState(false);
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant="ghost">Show Utilities</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-5xl">
<DialogHeader>
<DialogTitle>Utilities </DialogTitle>
<DialogDescription>Modify the application data</DialogDescription>
</DialogHeader>
<Tabs defaultValue="isolated">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="isolated">Isolated Deployment</TabsTrigger>
<TabsTrigger value="randomize">Randomize Compose</TabsTrigger>
</TabsList>
<TabsContent value="randomize" className="pt-5">
<RandomizeCompose composeId={composeId} />
</TabsContent>
<TabsContent value="isolated" className="pt-5">
<IsolatedDeployment composeId={composeId} />
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1 @@
ALTER TABLE "compose" RENAME COLUMN "deployable" TO "isolatedDeployment";

File diff suppressed because it is too large Load Diff

View File

@@ -456,6 +456,13 @@
"when": 1738564387043, "when": 1738564387043,
"tag": "0064_previous_agent_brand", "tag": "0064_previous_agent_brand",
"breakpoints": true "breakpoints": true
},
{
"idx": 65,
"version": "7",
"when": 1739087857244,
"tag": "0065_daily_zaladane",
"breakpoints": true
} }
] ]
} }

View File

@@ -46,7 +46,7 @@ import {
findServerById, findServerById,
loadServices, loadServices,
randomizeComposeFile, randomizeComposeFile,
randomizeDeployableComposeFile, randomizeIsolatedDeploymentComposeFile,
removeCompose, removeCompose,
removeComposeDirectory, removeComposeDirectory,
removeDeploymentsByComposeId, removeDeploymentsByComposeId,
@@ -217,7 +217,7 @@ export const composeRouter = createTRPCRouter({
} }
return await randomizeComposeFile(input.composeId, input.suffix); return await randomizeComposeFile(input.composeId, input.suffix);
}), }),
randomizeDeployableCompose: protectedProcedure isolatedDeployment: protectedProcedure
.input(apiRandomizeCompose) .input(apiRandomizeCompose)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId); const compose = await findComposeById(input.composeId);
@@ -227,9 +227,9 @@ export const composeRouter = createTRPCRouter({
message: "You are not authorized to randomize this compose", message: "You are not authorized to randomize this compose",
}); });
} }
return await randomizeDeployableComposeFile( return await randomizeIsolatedDeploymentComposeFile(
input.composeId, input.composeId,
input.suffix, input.suffix
); );
}), }),
getConvertedCompose: protectedProcedure getConvertedCompose: protectedProcedure
@@ -280,7 +280,7 @@ export const composeRouter = createTRPCRouter({
{ {
removeOnComplete: true, removeOnComplete: true,
removeOnFail: true, removeOnFail: true,
}, }
); );
}), }),
redeploy: protectedProcedure redeploy: protectedProcedure
@@ -312,7 +312,7 @@ export const composeRouter = createTRPCRouter({
{ {
removeOnComplete: true, removeOnComplete: true,
removeOnFail: true, removeOnFail: true,
}, }
); );
}), }),
stop: protectedProcedure stop: protectedProcedure

View File

@@ -62,14 +62,14 @@ export const compose = pgTable("compose", {
() => sshKeys.sshKeyId, () => sshKeys.sshKeyId,
{ {
onDelete: "set null", onDelete: "set null",
}, }
), ),
command: text("command").notNull().default(""), command: text("command").notNull().default(""),
// //
composePath: text("composePath").notNull().default("./docker-compose.yml"), composePath: text("composePath").notNull().default("./docker-compose.yml"),
suffix: text("suffix").notNull().default(""), suffix: text("suffix").notNull().default(""),
randomize: boolean("randomize").notNull().default(false), randomize: boolean("randomize").notNull().default(false),
deployable: boolean("deployable").notNull().default(false), isolatedDeployment: boolean("isolatedDeployment").notNull().default(false),
composeStatus: applicationStatus("composeStatus").notNull().default("idle"), composeStatus: applicationStatus("composeStatus").notNull().default("idle"),
projectId: text("projectId") projectId: text("projectId")
.notNull() .notNull()

View File

@@ -34,9 +34,9 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
await writeDomainsToCompose(compose, domains); await writeDomainsToCompose(compose, domains);
createEnvFile(compose); createEnvFile(compose);
if (compose.deployable) { if (compose.isolatedDeployment) {
await execAsync( await execAsync(
`docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}`, `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}`
); );
} }
@@ -76,12 +76,12 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
...getEnviromentVariablesObject(compose.env, compose.project.env), ...getEnviromentVariablesObject(compose.env, compose.project.env),
}), }),
}, },
}, }
); );
if (compose.deployable) { if (compose.isolatedDeployment) {
await execAsync( await execAsync(
`docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`, `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`
); );
} }
@@ -96,7 +96,7 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
export const getBuildComposeCommand = async ( export const getBuildComposeCommand = async (
compose: ComposeNested, compose: ComposeNested,
logPath: string, logPath: string
) => { ) => {
const { COMPOSE_PATH } = paths(true); const { COMPOSE_PATH } = paths(true);
const { sourceType, appName, mounts, composeType, domains, composePath } = const { sourceType, appName, mounts, composeType, domains, composePath } =
@@ -109,7 +109,7 @@ export const getBuildComposeCommand = async (
const newCompose = await writeDomainsToComposeRemote( const newCompose = await writeDomainsToComposeRemote(
compose, compose,
domains, domains,
logPath, logPath
); );
const logContent = ` const logContent = `
App Name: ${appName} App Name: ${appName}
@@ -141,9 +141,9 @@ Compose Type: ${composeType} ✅`;
cd "${projectPath}"; cd "${projectPath}";
${exportEnvCommand} ${exportEnvCommand}
${compose.deployable ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` : ""} ${compose.isolatedDeployment ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` : ""}
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; } docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
${compose.deployable ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""} ${compose.isolatedDeployment ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""}
echo "Docker Compose Deployed: ✅" >> "${logPath}" echo "Docker Compose Deployed: ✅" >> "${logPath}"
} || { } || {
@@ -203,7 +203,7 @@ const createEnvFile = (compose: ComposeNested) => {
const envFileContent = prepareEnvironmentVariables( const envFileContent = prepareEnvironmentVariables(
envContent, envContent,
compose.project.env, compose.project.env
).join("\n"); ).join("\n");
if (!existsSync(dirname(envFilePath))) { if (!existsSync(dirname(envFilePath))) {
@@ -232,7 +232,7 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
const envFileContent = prepareEnvironmentVariables( const envFileContent = prepareEnvironmentVariables(
envContent, envContent,
compose.project.env, compose.project.env
).join("\n"); ).join("\n");
const encodedContent = encodeBase64(envFileContent); const encodedContent = encodeBase64(envFileContent);
@@ -247,7 +247,7 @@ const getExportEnvCommand = (compose: ComposeNested) => {
const envVars = getEnviromentVariablesObject( const envVars = getEnviromentVariablesObject(
compose.env, compose.env,
compose.project.env, compose.project.env
); );
const exports = Object.entries(envVars) const exports = Object.entries(envVars)
.map(([key, value]) => `export ${key}=${JSON.stringify(value)}`) .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)

View File

@@ -7,7 +7,7 @@ import type { ComposeSpecification } from "./types";
export const addAppNameToPreventCollision = ( export const addAppNameToPreventCollision = (
composeData: ComposeSpecification, composeData: ComposeSpecification,
appName: string, appName: string
): ComposeSpecification => { ): ComposeSpecification => {
let updatedComposeData = { ...composeData }; let updatedComposeData = { ...composeData };
@@ -16,9 +16,9 @@ export const addAppNameToPreventCollision = (
return updatedComposeData; return updatedComposeData;
}; };
export const randomizeDeployableComposeFile = async ( export const randomizeIsolatedDeploymentComposeFile = async (
composeId: string, composeId: string,
suffix?: string, suffix?: string
) => { ) => {
const compose = await findComposeById(composeId); const compose = await findComposeById(composeId);
const composeFile = compose.composeFile; const composeFile = compose.composeFile;
@@ -28,7 +28,7 @@ export const randomizeDeployableComposeFile = async (
const newComposeFile = addAppNameToPreventCollision( const newComposeFile = addAppNameToPreventCollision(
composeData, composeData,
randomSuffix, randomSuffix
); );
return dump(newComposeFile); return dump(newComposeFile);
@@ -36,7 +36,7 @@ export const randomizeDeployableComposeFile = async (
export const randomizeDeployableSpecificationFile = ( export const randomizeDeployableSpecificationFile = (
composeSpec: ComposeSpecification, composeSpec: ComposeSpecification,
suffix?: string, suffix?: string
) => { ) => {
if (!suffix) { if (!suffix) {
return composeSpec; return composeSpec;

View File

@@ -78,7 +78,7 @@ export const getComposePath = (compose: Compose) => {
}; };
export const loadDockerCompose = async ( export const loadDockerCompose = async (
compose: Compose, compose: Compose
): Promise<ComposeSpecification | null> => { ): Promise<ComposeSpecification | null> => {
const path = getComposePath(compose); const path = getComposePath(compose);
@@ -91,7 +91,7 @@ export const loadDockerCompose = async (
}; };
export const loadDockerComposeRemote = async ( export const loadDockerComposeRemote = async (
compose: Compose, compose: Compose
): Promise<ComposeSpecification | null> => { ): Promise<ComposeSpecification | null> => {
const path = getComposePath(compose); const path = getComposePath(compose);
try { try {
@@ -100,7 +100,7 @@ export const loadDockerComposeRemote = async (
} }
const { stdout, stderr } = await execAsyncRemote( const { stdout, stderr } = await execAsyncRemote(
compose.serverId, compose.serverId,
`cat ${path}`, `cat ${path}`
); );
if (stderr) { if (stderr) {
@@ -125,7 +125,7 @@ export const readComposeFile = async (compose: Compose) => {
export const writeDomainsToCompose = async ( export const writeDomainsToCompose = async (
compose: Compose, compose: Compose,
domains: Domain[], domains: Domain[]
) => { ) => {
if (!domains.length) { if (!domains.length) {
return; return;
@@ -144,7 +144,7 @@ export const writeDomainsToCompose = async (
export const writeDomainsToComposeRemote = async ( export const writeDomainsToComposeRemote = async (
compose: Compose, compose: Compose,
domains: Domain[], domains: Domain[],
logPath: string, logPath: string
) => { ) => {
if (!domains.length) { if (!domains.length) {
return ""; return "";
@@ -175,7 +175,7 @@ exit 1;
// (node:59875) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit // (node:59875) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit
export const addDomainToCompose = async ( export const addDomainToCompose = async (
compose: Compose, compose: Compose,
domains: Domain[], domains: Domain[]
) => { ) => {
const { appName } = compose; const { appName } = compose;
@@ -191,10 +191,10 @@ export const addDomainToCompose = async (
return null; return null;
} }
if (compose.deployable) { if (compose.isolatedDeployment) {
const randomized = randomizeDeployableSpecificationFile( const randomized = randomizeDeployableSpecificationFile(
result, result,
compose.suffix || compose.appName, compose.suffix || compose.appName
); );
result = randomized; result = randomized;
} else if (compose.randomize) { } else if (compose.randomize) {
@@ -216,7 +216,7 @@ export const addDomainToCompose = async (
const httpsLabels = await createDomainLabels( const httpsLabels = await createDomainLabels(
appName, appName,
domain, domain,
"websecure", "websecure"
); );
httpLabels.push(...httpsLabels); httpLabels.push(...httpsLabels);
} }
@@ -247,16 +247,16 @@ export const addDomainToCompose = async (
labels.push(...httpLabels); labels.push(...httpLabels);
} }
if (!compose.deployable) { if (!compose.isolatedDeployment) {
// Add the dokploy-network to the service // Add the dokploy-network to the service
result.services[serviceName].networks = addDokployNetworkToService( result.services[serviceName].networks = addDokployNetworkToService(
result.services[serviceName].networks, result.services[serviceName].networks
); );
} }
} }
// Add dokploy-network to the root of the compose file // Add dokploy-network to the root of the compose file
if (!compose.deployable) { if (!compose.isolatedDeployment) {
result.networks = addDokployNetworkToRoot(result.networks); result.networks = addDokployNetworkToRoot(result.networks);
} }
@@ -265,7 +265,7 @@ export const addDomainToCompose = async (
export const writeComposeFile = async ( export const writeComposeFile = async (
compose: Compose, compose: Compose,
composeSpec: ComposeSpecification, composeSpec: ComposeSpecification
) => { ) => {
const path = getComposePath(compose); const path = getComposePath(compose);
@@ -282,7 +282,7 @@ export const writeComposeFile = async (
export const createDomainLabels = async ( export const createDomainLabels = async (
appName: string, appName: string,
domain: Domain, domain: Domain,
entrypoint: "web" | "websecure", entrypoint: "web" | "websecure"
) => { ) => {
const { host, port, https, uniqueConfigKey, certificateType, path } = domain; const { host, port, https, uniqueConfigKey, certificateType, path } = domain;
const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`; const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`;
@@ -295,14 +295,14 @@ export const createDomainLabels = async (
if (entrypoint === "web" && https) { if (entrypoint === "web" && https) {
labels.push( labels.push(
`traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`, `traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`
); );
} }
if (entrypoint === "websecure") { if (entrypoint === "websecure") {
if (certificateType === "letsencrypt") { if (certificateType === "letsencrypt") {
labels.push( labels.push(
`traefik.http.routers.${routerName}.tls.certresolver=letsencrypt`, `traefik.http.routers.${routerName}.tls.certresolver=letsencrypt`
); );
} }
} }
@@ -311,7 +311,7 @@ export const createDomainLabels = async (
}; };
export const addDokployNetworkToService = ( export const addDokployNetworkToService = (
networkService: DefinitionsService["networks"], networkService: DefinitionsService["networks"]
) => { ) => {
let networks = networkService; let networks = networkService;
const network = "dokploy-network"; const network = "dokploy-network";
@@ -333,7 +333,7 @@ export const addDokployNetworkToService = (
}; };
export const addDokployNetworkToRoot = ( export const addDokployNetworkToRoot = (
networkRoot: PropertiesNetworks | undefined, networkRoot: PropertiesNetworks | undefined
) => { ) => {
let networks = networkRoot; let networks = networkRoot;
const network = "dokploy-network"; const network = "dokploy-network";