@@ -11,74 +10,67 @@
Dokploy is a free self-hostable Platform as a Service (PaaS) that simplifies the deployment and management of applications and databases.
-
### Features
Dokploy include multiples features to make your life easier.
-
-* **Applications**: Deploy any type of application (Node.js, PHP, Python, Go, Ruby, etc.).
-* **Databases**: Create and manage databases with support for MySQL, PostgreSQL, MongoDB, MariaDB, Redis.
-* **Backups**: Automate backups for databases to a external storage destination.
-* **Docker Compose**: Native support for Docker Compose to manage complex applications.
-* **Multi Node**: Scale applications to multiples nodes using docker swarm to manage the cluster.
-* **Templates**: Deploy in a single click open source templates (Plausible, Pocketbase, Calcom, etc.).
-* **Traefik Integration**: Automatically integrates with Traefik for routing and load balancing.
-* **Real-time Monitoring**: Monitor CPU, memory, storage, and network usage, for every resource.
-* **Docker Management**: Easily deploy and manage Docker containers.
-* **CLI/API**: Manage your applications and databases using the command line or trought the API.
-* **Self-Hosted**: Self-host Dokploy on your VPS.
-
-
-
+- **Applications**: Deploy any type of application (Node.js, PHP, Python, Go, Ruby, etc.).
+- **Databases**: Create and manage databases with support for MySQL, PostgreSQL, MongoDB, MariaDB, Redis.
+- **Backups**: Automate backups for databases to a external storage destination.
+- **Docker Compose**: Native support for Docker Compose to manage complex applications.
+- **Multi Node**: Scale applications to multiples nodes using docker swarm to manage the cluster.
+- **Templates**: Deploy in a single click open source templates (Plausible, Pocketbase, Calcom, etc.).
+- **Traefik Integration**: Automatically integrates with Traefik for routing and load balancing.
+- **Real-time Monitoring**: Monitor CPU, memory, storage, and network usage, for every resource.
+- **Docker Management**: Easily deploy and manage Docker containers.
+- **CLI/API**: Manage your applications and databases using the command line or trought the API.
+- **Self-Hosted**: Self-host Dokploy on your VPS.
## 🚀 Getting Started
To get started run the following command in a VPS:
-
```bash
curl -sSL https://dokploy.com/install.sh | sh
```
-
## 📄 Documentation
For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
-
## Video Tutorial
+
-
## Donations
If you like dokploy, and want to support the project to cover the costs of hosting, testing and development new features, you can donate to the project using the following link:
Thanks to all the supporters!
-https://opencollective.com/dokploy
+[Dokploy Open Collective](https://opencollective.com/dokploy)
+Organizations:
+

+
+Individuals:

-
## Contributors
-
-
## Support OS
-- Ubuntu 24.04 LTS
+- Ubuntu 24.04 LTS
- Ubuntu 23.10
-- Ubuntu 22.04 LTS
-- Ubuntu 20.04 LTS
+- Ubuntu 22.04 LTS
+- Ubuntu 20.04 LTS
- Ubuntu 18.04 LTS
- Debian 12
- Debian 11
@@ -86,9 +78,6 @@ https://opencollective.com/dokploy
- Centos 9
- Centos 8
-
-
## Explanation
+
[English](README.md) | [中文](README-zh.md) | [Deutsch](README-de.md) | [Русский Язык](README-ru.md)
-
-
diff --git a/__test__/compose/compose.test.ts b/__test__/compose/compose.test.ts
index 675cb7729..1e728b997 100644
--- a/__test__/compose/compose.test.ts
+++ b/__test__/compose/compose.test.ts
@@ -1,7 +1,7 @@
-import { expect, test } from "vitest";
-import { load } from "js-yaml";
import { addPrefixToAllProperties } from "@/server/utils/docker/compose";
import type { ComposeSpecification } from "@/server/utils/docker/types";
+import { load } from "js-yaml";
+import { expect, test } from "vitest";
const composeFile1 = `
version: "3.8"
diff --git a/__test__/compose/network/network-service.test.ts b/__test__/compose/network/network-service.test.ts
index 92df7a73d..d592811b5 100644
--- a/__test__/compose/network/network-service.test.ts
+++ b/__test__/compose/network/network-service.test.ts
@@ -79,10 +79,11 @@ test("Add prefix to networks in services with aliases", () => {
`frontend-${prefix}`,
);
- const networkConfig =
- actualComposeData?.services?.api?.networks[`frontend-${prefix}`];
- expect(networkConfig).toBeDefined();
- expect(networkConfig?.aliases).toContain("api");
+ const networkConfig = actualComposeData?.services?.api?.networks as {
+ [key: string]: { aliases?: string[] };
+ };
+ expect(networkConfig[`frontend-${prefix}`]).toBeDefined();
+ expect(networkConfig[`frontend-${prefix}`]?.aliases).toContain("api");
expect(actualComposeData.services?.api?.networks).not.toHaveProperty(
"frontend-ash",
@@ -169,7 +170,9 @@ test("Add prefix to networks in services (combined case)", () => {
);
// Caso 2: Objeto con aliases
- const apiNetworks = actualComposeData.services?.api?.networks;
+ const apiNetworks = actualComposeData.services?.api?.networks as {
+ [key: string]: unknown;
+ };
expect(apiNetworks).toHaveProperty(`frontend-${prefix}`);
expect(apiNetworks[`frontend-${prefix}`]).toBeDefined();
expect(apiNetworks).not.toHaveProperty("frontend");
diff --git a/__test__/compose/network/network.test.ts b/__test__/compose/network/network.test.ts
index ae9387757..f86cabfde 100644
--- a/__test__/compose/network/network.test.ts
+++ b/__test__/compose/network/network.test.ts
@@ -76,9 +76,11 @@ test("Add prefix to networks in services and root (combined case)", () => {
);
// Caso 2: Objeto con aliases
- const apiNetworks = actualComposeData.services?.api?.networks;
+ const apiNetworks = actualComposeData.services?.api?.networks as {
+ [key: string]: { aliases?: string[] };
+ };
expect(apiNetworks).toHaveProperty(`frontend-${prefix}`);
- expect(apiNetworks[`frontend-${prefix}`]?.aliases).toContain("api");
+ expect(apiNetworks?.[`frontend-${prefix}`]?.aliases).toContain("api");
expect(apiNetworks).not.toHaveProperty("frontend");
// Caso 3: Objeto con redes simples
diff --git a/__test__/compose/secrets/secret-root.test.ts b/__test__/compose/secrets/secret-root.test.ts
index 861343a26..61f9f818d 100644
--- a/__test__/compose/secrets/secret-root.test.ts
+++ b/__test__/compose/secrets/secret-root.test.ts
@@ -1,8 +1,8 @@
-import { expect, test } from "vitest";
-import { load, dump } from "js-yaml";
import { generateRandomHash } from "@/server/utils/docker/compose";
-import type { ComposeSpecification } from "@/server/utils/docker/types";
import { addPrefixToSecretsRoot } from "@/server/utils/docker/compose/secrets";
+import type { ComposeSpecification } from "@/server/utils/docker/types";
+import { dump, load } from "js-yaml";
+import { expect, test } from "vitest";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
diff --git a/__test__/compose/service/service-container-name.test.ts b/__test__/compose/service/service-container-name.test.ts
index 9e3bfa80c..9f5fe9ed2 100644
--- a/__test__/compose/service/service-container-name.test.ts
+++ b/__test__/compose/service/service-container-name.test.ts
@@ -42,7 +42,7 @@ test("Add prefix to service names with container_name in compose file", () => {
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que el nombre del contenedor ha cambiado correctamente
- expect(actualComposeData.services[`web-${prefix}`].container_name).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.container_name).toBe(
`web_container-${prefix}`,
);
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
@@ -50,10 +50,10 @@ test("Add prefix to service names with container_name in compose file", () => {
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
- expect(actualComposeData.services[`web-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.image).toBe(
"nginx:latest",
);
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
});
diff --git a/__test__/compose/service/service-depends-on.test.ts b/__test__/compose/service/service-depends-on.test.ts
index cf4ca1328..e339ee65a 100644
--- a/__test__/compose/service/service-depends-on.test.ts
+++ b/__test__/compose/service/service-depends-on.test.ts
@@ -51,30 +51,30 @@ test("Add prefix to service names with depends_on (array) in compose file", () =
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
- expect(actualComposeData.services[`web-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.image).toBe(
"nginx:latest",
);
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en depends_on tienen el prefijo
- expect(actualComposeData.services[`web-${prefix}`].depends_on).toContain(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.depends_on).toContain(
`db-${prefix}`,
);
- expect(actualComposeData.services[`web-${prefix}`].depends_on).toContain(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.depends_on).toContain(
`api-${prefix}`,
);
// Verificar que los servicios `db` y `api` también tienen el prefijo
expect(actualComposeData.services).toHaveProperty(`db-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("db");
- expect(actualComposeData.services[`db-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`db-${prefix}`]?.image).toBe(
"postgres:latest",
);
expect(actualComposeData.services).toHaveProperty(`api-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("api");
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
});
@@ -121,16 +121,16 @@ test("Add prefix to service names with depends_on (object) in compose file", ()
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
- expect(actualComposeData.services[`web-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.image).toBe(
"nginx:latest",
);
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en depends_on tienen el prefijo
- const webDependsOn = actualComposeData.services[`web-${prefix}`]
- .depends_on as Record
;
+ const webDependsOn = actualComposeData.services?.[`web-${prefix}`]
+ ?.depends_on as Record;
expect(webDependsOn).toHaveProperty(`db-${prefix}`);
expect(webDependsOn).toHaveProperty(`api-${prefix}`);
expect(webDependsOn[`db-${prefix}`].condition).toBe("service_healthy");
@@ -139,12 +139,12 @@ test("Add prefix to service names with depends_on (object) in compose file", ()
// Verificar que los servicios `db` y `api` también tienen el prefijo
expect(actualComposeData.services).toHaveProperty(`db-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("db");
- expect(actualComposeData.services[`db-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`db-${prefix}`]?.image).toBe(
"postgres:latest",
);
expect(actualComposeData.services).toHaveProperty(`api-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("api");
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
});
diff --git a/__test__/compose/service/service-extends.test.ts b/__test__/compose/service/service-extends.test.ts
index 6188e4a8f..e8f31aab5 100644
--- a/__test__/compose/service/service-extends.test.ts
+++ b/__test__/compose/service/service-extends.test.ts
@@ -49,22 +49,22 @@ test("Add prefix to service names with extends (string) in compose file", () =>
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
- expect(actualComposeData.services[`web-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.image).toBe(
"nginx:latest",
);
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que el nombre en extends tiene el prefijo
- expect(actualComposeData.services[`web-${prefix}`].extends).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.extends).toBe(
`base_service-${prefix}`,
);
// Verificar que el servicio `base_service` también tiene el prefijo
expect(actualComposeData.services).toHaveProperty(`base_service-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("base_service");
- expect(actualComposeData.services[`base_service-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`base_service-${prefix}`]?.image).toBe(
"base:latest",
);
});
@@ -109,23 +109,23 @@ test("Add prefix to service names with extends (object) in compose file", () =>
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
- expect(actualComposeData.services[`web-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.image).toBe(
"nginx:latest",
);
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que el nombre en extends.service tiene el prefijo
- const webExtends = actualComposeData.services[`web-${prefix}`].extends;
+ const webExtends = actualComposeData.services?.[`web-${prefix}`]?.extends;
if (typeof webExtends !== "string") {
- expect(webExtends.service).toBe(`base_service-${prefix}`);
+ expect(webExtends?.service).toBe(`base_service-${prefix}`);
}
// Verificar que el servicio `base_service` también tiene el prefijo
expect(actualComposeData.services).toHaveProperty(`base_service-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("base_service");
- expect(actualComposeData.services[`base_service-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`base_service-${prefix}`]?.image).toBe(
"base:latest",
);
});
diff --git a/__test__/compose/service/service-links.test.ts b/__test__/compose/service/service-links.test.ts
index b9b22fdf3..082526496 100644
--- a/__test__/compose/service/service-links.test.ts
+++ b/__test__/compose/service/service-links.test.ts
@@ -50,27 +50,27 @@ test("Add prefix to service names with links in compose file", () => {
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
- expect(actualComposeData.services[`web-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.image).toBe(
"nginx:latest",
);
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en links tienen el prefijo
- expect(actualComposeData.services[`web-${prefix}`].links).toContain(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.links).toContain(
`db-${prefix}`,
);
// Verificar que los servicios `db` y `api` también tienen el prefijo
expect(actualComposeData.services).toHaveProperty(`db-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("db");
- expect(actualComposeData.services[`db-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`db-${prefix}`]?.image).toBe(
"postgres:latest",
);
expect(actualComposeData.services).toHaveProperty(`api-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("api");
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
});
diff --git a/__test__/compose/service/sevice-volumes-from.test.ts b/__test__/compose/service/sevice-volumes-from.test.ts
index 90905a00e..00d75fe82 100644
--- a/__test__/compose/service/sevice-volumes-from.test.ts
+++ b/__test__/compose/service/sevice-volumes-from.test.ts
@@ -54,23 +54,25 @@ test("Add prefix to service names with volumes_from in compose file", () => {
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
- expect(actualComposeData.services[`web-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.image).toBe(
"nginx:latest",
);
- expect(actualComposeData.services[`api-${prefix}`].image).toBe(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en volumes_from tienen el prefijo
- expect(actualComposeData.services[`web-${prefix}`].volumes_from).toContain(
+ expect(actualComposeData.services?.[`web-${prefix}`]?.volumes_from).toContain(
`shared-${prefix}`,
);
- expect(actualComposeData.services[`api-${prefix}`].volumes_from).toContain(
+ expect(actualComposeData.services?.[`api-${prefix}`]?.volumes_from).toContain(
`shared-${prefix}`,
);
// Verificar que el servicio shared también tiene el prefijo
expect(actualComposeData.services).toHaveProperty(`shared-${prefix}`);
expect(actualComposeData.services).not.toHaveProperty("shared");
- expect(actualComposeData.services[`shared-${prefix}`].image).toBe("busybox");
+ expect(actualComposeData.services?.[`shared-${prefix}`]?.image).toBe(
+ "busybox",
+ );
});
diff --git a/__test__/compose/volume/volume-2.test.ts b/__test__/compose/volume/volume-2.test.ts
index 41581844e..57bfb7618 100644
--- a/__test__/compose/volume/volume-2.test.ts
+++ b/__test__/compose/volume/volume-2.test.ts
@@ -1,7 +1,7 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import {
- addPrefixToVolumesRoot,
addPrefixToAllVolumes,
+ addPrefixToVolumesRoot,
} from "@/server/utils/docker/compose/volume";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { load } from "js-yaml";
diff --git a/__test__/vitest.config.ts b/__test__/vitest.config.ts
index 4127903f8..71749b6c2 100644
--- a/__test__/vitest.config.ts
+++ b/__test__/vitest.config.ts
@@ -1,5 +1,5 @@
-import { defineConfig } from "vitest/config";
import tsconfigPaths from "vite-tsconfig-paths";
+import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [
diff --git a/biome.json b/biome.json
index dd7c1eb71..fd2d79a31 100644
--- a/biome.json
+++ b/biome.json
@@ -1,17 +1,34 @@
{
- "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
- "linter":{
- "rules": {
- "correctness":{
- "useExhaustiveDependencies": "off"
- },
- "suspicious":{
- "noArrayIndexKey": "off"
- },
- "a11y":{
- "noSvgWithoutTitle":"off"
- }
- }
- }
-
-}
\ No newline at end of file
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
+ "files": {
+ "ignore": ["node_modules/**", ".next/**", "drizzle/**", ".docker"]
+ },
+ "organizeImports": {
+ "enabled": true
+ },
+ "linter": {
+ "rules": {
+ "complexity": {
+ "noUselessCatch": "off",
+ "noBannedTypes": "off"
+ },
+ "correctness": {
+ "useExhaustiveDependencies": "off",
+ "noUnsafeOptionalChaining": "off"
+ },
+ "style": {
+ "noNonNullAssertion": "off"
+ },
+ "suspicious": {
+ "noArrayIndexKey": "off",
+ "noExplicitAny": "off",
+ "noRedeclare": "off"
+ },
+ "a11y": {
+ "noSvgWithoutTitle": "off",
+ "useKeyWithClickEvents": "off",
+ "useAriaPropsForRole": "off"
+ }
+ }
+ }
+}
diff --git a/components.json b/components.json
index b4baac4f4..81104c1e9 100644
--- a/components.json
+++ b/components.json
@@ -1,17 +1,17 @@
{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "default",
- "rsc": false,
- "tsx": true,
- "tailwind": {
- "config": "tailwind.config.ts",
- "css": "styles/globals.css",
- "baseColor": "zinc",
- "cssVariables": true,
- "prefix": ""
- },
- "aliases": {
- "components": "@/components",
- "utils": "@/lib/utils"
- }
-}
\ No newline at end of file
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "styles/globals.css",
+ "baseColor": "zinc",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils"
+ }
+}
diff --git a/components/auth/login-2fa.tsx b/components/auth/login-2fa.tsx
index 6bf03d0c3..7c4915fa6 100644
--- a/components/auth/login-2fa.tsx
+++ b/components/auth/login-2fa.tsx
@@ -10,19 +10,19 @@ import {
} from "@/components/ui/form";
import { CardTitle } from "@/components/ui/card";
-import { api } from "@/utils/api";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { AlertTriangle } from "lucide-react";
-import { useEffect } from "react";
-import { useForm } from "react-hook-form";
-import { toast } from "sonner";
-import { z } from "zod";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
+import { api } from "@/utils/api";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { AlertTriangle } from "lucide-react";
import { useRouter } from "next/router";
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+import { z } from "zod";
const Login2FASchema = z.object({
pin: z.string().min(6, {
diff --git a/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx b/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx
index 3e2730c9f..fd91703b2 100644
--- a/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx
+++ b/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx
@@ -1,3 +1,5 @@
+import { AlertBlock } from "@/components/shared/alert-block";
+import { CodeEditor } from "@/components/shared/code-editor";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -17,21 +19,19 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
-import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { useEffect } from "react";
-import { useForm } from "react-hook-form";
-import { toast } from "sonner";
-import { z } from "zod";
-import { HelpCircle, Settings } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
-import { CodeEditor } from "@/components/shared/code-editor";
+import { api } from "@/utils/api";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { HelpCircle, Settings } from "lucide-react";
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+import { z } from "zod";
const HealthCheckSwarmSchema = z
.object({
diff --git a/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx b/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx
index da69aedb8..4078ae4c1 100644
--- a/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx
+++ b/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx
@@ -1,4 +1,5 @@
-import React from "react";
+import { AlertBlock } from "@/components/shared/alert-block";
+import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
@@ -6,8 +7,6 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-import { api } from "@/utils/api";
-import { z } from "zod";
import {
Form,
FormControl,
@@ -16,11 +15,6 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
-import { toast } from "sonner";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { useForm } from "react-hook-form";
-import { useEffect } from "react";
-import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Select,
@@ -31,10 +25,16 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
-import Link from "next/link";
+import { api } from "@/utils/api";
+import { zodResolver } from "@hookform/resolvers/zod";
import { Server } from "lucide-react";
+import Link from "next/link";
+import React from "react";
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+import { z } from "zod";
import { AddSwarmSettings } from "./modify-swarm-settings";
-import { AlertBlock } from "@/components/shared/alert-block";
interface Props {
applicationId: string;
diff --git a/components/dashboard/application/advanced/general/add-command.tsx b/components/dashboard/application/advanced/general/add-command.tsx
index a898607d7..979660bcc 100644
--- a/components/dashboard/application/advanced/general/add-command.tsx
+++ b/components/dashboard/application/advanced/general/add-command.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
@@ -6,8 +6,6 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-import { api } from "@/utils/api";
-import { z } from "zod";
import {
Form,
FormControl,
@@ -16,12 +14,14 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
-import { toast } from "sonner";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { useForm } from "react-hook-form";
-import { useEffect } from "react";
-import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
+import { api } from "@/utils/api";
+import { zodResolver } from "@hookform/resolvers/zod";
+import React from "react";
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+import { z } from "zod";
interface Props {
applicationId: string;
}
diff --git a/components/dashboard/application/advanced/ports/add-port.tsx b/components/dashboard/application/advanced/ports/add-port.tsx
index 76939d821..873baa679 100644
--- a/components/dashboard/application/advanced/ports/add-port.tsx
+++ b/components/dashboard/application/advanced/ports/add-port.tsx
@@ -1,3 +1,4 @@
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -17,13 +18,6 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
-import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { useEffect } from "react";
-import { useForm } from "react-hook-form";
-import { toast } from "sonner";
-import { PlusIcon } from "lucide-react";
import {
Select,
SelectContent,
@@ -31,6 +25,12 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
+import { api } from "@/utils/api";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { PlusIcon } from "lucide-react";
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
import { z } from "zod";
const AddPortSchema = z.object({
diff --git a/components/dashboard/application/advanced/ports/show-port.tsx b/components/dashboard/application/advanced/ports/show-port.tsx
index ab5f40970..1ab804fb4 100644
--- a/components/dashboard/application/advanced/ports/show-port.tsx
+++ b/components/dashboard/application/advanced/ports/show-port.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import { AlertBlock } from "@/components/shared/alert-block";
import {
Card,
CardContent,
@@ -8,10 +8,10 @@ import {
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { Rss } from "lucide-react";
+import React from "react";
import { AddPort } from "./add-port";
import { DeletePort } from "./delete-port";
import { UpdatePort } from "./update-port";
-import { AlertBlock } from "@/components/shared/alert-block";
interface Props {
applicationId: string;
}
diff --git a/components/dashboard/application/advanced/ports/update-port.tsx b/components/dashboard/application/advanced/ports/update-port.tsx
index 9742964e2..8f9d9cd7f 100644
--- a/components/dashboard/application/advanced/ports/update-port.tsx
+++ b/components/dashboard/application/advanced/ports/update-port.tsx
@@ -1,3 +1,4 @@
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -17,14 +18,6 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
-import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { PenBoxIcon, Pencil } from "lucide-react";
-import { useEffect } from "react";
-import { useForm } from "react-hook-form";
-import { toast } from "sonner";
-import { z } from "zod";
import {
Select,
SelectContent,
@@ -32,6 +25,13 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
+import { api } from "@/utils/api";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { PenBoxIcon, Pencil } from "lucide-react";
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+import { z } from "zod";
const UpdatePortSchema = z.object({
publishedPort: z.number().int().min(1).max(65535),
diff --git a/components/dashboard/application/advanced/redirects/add-redirect.tsx b/components/dashboard/application/advanced/redirects/add-redirect.tsx
index 4bcf2c567..661990a97 100644
--- a/components/dashboard/application/advanced/redirects/add-redirect.tsx
+++ b/components/dashboard/application/advanced/redirects/add-redirect.tsx
@@ -1,3 +1,4 @@
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -11,22 +12,21 @@ import {
import {
Form,
FormControl,
+ FormDescription,
FormField,
FormItem,
FormLabel,
- FormDescription,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
+import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
+import { PlusIcon } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
-import { PlusIcon } from "lucide-react";
import { z } from "zod";
-import { Switch } from "@/components/ui/switch";
const AddRedirectchema = z.object({
regex: z.string().min(1, "Regex required"),
diff --git a/components/dashboard/application/advanced/redirects/show-redirects.tsx b/components/dashboard/application/advanced/redirects/show-redirects.tsx
index 2a6e80a22..9a8325fc8 100644
--- a/components/dashboard/application/advanced/redirects/show-redirects.tsx
+++ b/components/dashboard/application/advanced/redirects/show-redirects.tsx
@@ -1,4 +1,3 @@
-import React from "react";
import {
Card,
CardContent,
@@ -8,6 +7,7 @@ import {
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { Split } from "lucide-react";
+import React from "react";
import { AddRedirect } from "./add-redirect";
import { DeleteRedirect } from "./delete-redirect";
import { UpdateRedirect } from "./update-redirect";
diff --git a/components/dashboard/application/advanced/redirects/update-redirect.tsx b/components/dashboard/application/advanced/redirects/update-redirect.tsx
index 9643938bd..855f5c8c0 100644
--- a/components/dashboard/application/advanced/redirects/update-redirect.tsx
+++ b/components/dashboard/application/advanced/redirects/update-redirect.tsx
@@ -1,3 +1,4 @@
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -11,22 +12,21 @@ import {
import {
Form,
FormControl,
+ FormDescription,
FormField,
FormItem,
FormLabel,
- FormDescription,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
+import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { PenBoxIcon, Pencil } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
-import { Switch } from "@/components/ui/switch";
const UpdateRedirectSchema = z.object({
regex: z.string().min(1, "Regex required"),
permanent: z.boolean().default(false),
diff --git a/components/dashboard/application/advanced/security/add-security.tsx b/components/dashboard/application/advanced/security/add-security.tsx
index 1f7c0d392..64dfd57ab 100644
--- a/components/dashboard/application/advanced/security/add-security.tsx
+++ b/components/dashboard/application/advanced/security/add-security.tsx
@@ -1,3 +1,4 @@
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -18,12 +19,11 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
+import { PlusIcon } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
-import { PlusIcon } from "lucide-react";
import { z } from "zod";
const AddSecuritychema = z.object({
diff --git a/components/dashboard/application/advanced/security/show-security.tsx b/components/dashboard/application/advanced/security/show-security.tsx
index ef51e2c90..5c02bf76d 100644
--- a/components/dashboard/application/advanced/security/show-security.tsx
+++ b/components/dashboard/application/advanced/security/show-security.tsx
@@ -1,4 +1,3 @@
-import React from "react";
import {
Card,
CardContent,
@@ -8,6 +7,7 @@ import {
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { LockKeyhole } from "lucide-react";
+import React from "react";
import { AddSecurity } from "./add-security";
import { DeleteSecurity } from "./delete-security";
import { UpdateSecurity } from "./update-security";
diff --git a/components/dashboard/application/advanced/security/update-security.tsx b/components/dashboard/application/advanced/security/update-security.tsx
index 9ff53c2e0..bb6e59aeb 100644
--- a/components/dashboard/application/advanced/security/update-security.tsx
+++ b/components/dashboard/application/advanced/security/update-security.tsx
@@ -1,3 +1,4 @@
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -18,7 +19,6 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { PenBoxIcon, Pencil } from "lucide-react";
import { useEffect } from "react";
diff --git a/components/dashboard/application/advanced/show-application-advanced-settings.tsx b/components/dashboard/application/advanced/show-application-advanced-settings.tsx
index d01773319..56513465f 100644
--- a/components/dashboard/application/advanced/show-application-advanced-settings.tsx
+++ b/components/dashboard/application/advanced/show-application-advanced-settings.tsx
@@ -1,3 +1,4 @@
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Card,
@@ -21,7 +22,6 @@ import React, { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
-import { AlertBlock } from "@/components/shared/alert-block";
const addResourcesApplication = z.object({
memoryReservation: z.number().nullable().optional(),
diff --git a/components/dashboard/application/advanced/traefik/show-traefik-config.tsx b/components/dashboard/application/advanced/traefik/show-traefik-config.tsx
index a3fb4f302..28d442648 100644
--- a/components/dashboard/application/advanced/traefik/show-traefik-config.tsx
+++ b/components/dashboard/application/advanced/traefik/show-traefik-config.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import { CodeEditor } from "@/components/shared/code-editor";
import {
Card,
CardContent,
@@ -8,8 +8,8 @@ import {
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { File } from "lucide-react";
+import React from "react";
import { UpdateTraefikConfig } from "./update-traefik-config";
-import { CodeEditor } from "@/components/shared/code-editor";
interface Props {
applicationId: string;
}
diff --git a/components/dashboard/application/advanced/traefik/update-traefik-config.tsx b/components/dashboard/application/advanced/traefik/update-traefik-config.tsx
index 659353ec0..a185082be 100644
--- a/components/dashboard/application/advanced/traefik/update-traefik-config.tsx
+++ b/components/dashboard/application/advanced/traefik/update-traefik-config.tsx
@@ -1,3 +1,5 @@
+import { AlertBlock } from "@/components/shared/alert-block";
+import { CodeEditor } from "@/components/shared/code-editor";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -17,14 +19,12 @@ import {
FormMessage,
} from "@/components/ui/form";
import { api } from "@/utils/api";
-import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
+import jsyaml from "js-yaml";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
-import jsyaml from "js-yaml";
-import { CodeEditor } from "@/components/shared/code-editor";
const UpdateTraefikConfigSchema = z.object({
traefikConfig: z.string(),
@@ -110,12 +110,15 @@ export const UpdateTraefikConfig = ({ applicationId }: Props) => {
};
return (
-