From 3e2cfe6eb8b1549b4532ae4b61270007391f4ebf Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 9 Feb 2025 02:20:28 -0600 Subject: [PATCH] refactor: agroupate utilities --- .../compose/general/compose-file-editor.tsx | 6 +- .../compose/general/isolated-deployment.tsx | 191 + .../compose/general/randomize-compose.tsx | 239 +- .../compose/general/randomize-deployable.tsx | 195 - .../compose/general/show-utilities.tsx | 46 + apps/dokploy/drizzle/0065_daily_zaladane.sql | 1 + apps/dokploy/drizzle/meta/0065_snapshot.json | 4485 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/server/api/routers/compose.ts | 826 +-- packages/server/src/db/schema/compose.ts | 272 +- packages/server/src/utils/builders/compose.ts | 322 +- packages/server/src/utils/docker/collision.ts | 50 +- packages/server/src/utils/docker/domain.ts | 494 +- 13 files changed, 5826 insertions(+), 1308 deletions(-) create mode 100644 apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx delete mode 100644 apps/dokploy/components/dashboard/compose/general/randomize-deployable.tsx create mode 100644 apps/dokploy/components/dashboard/compose/general/show-utilities.tsx create mode 100644 apps/dokploy/drizzle/0065_daily_zaladane.sql create mode 100644 apps/dokploy/drizzle/meta/0065_snapshot.json diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx index 3e51cf05..47497219 100644 --- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx +++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx @@ -15,7 +15,8 @@ import { toast } from "sonner"; import { z } from "zod"; import { validateAndFormatYAML } from "../../application/advanced/traefik/update-traefik-config"; import { RandomizeCompose } from "./randomize-compose"; -import { RandomizeDeployable } from "./randomize-deployable"; +import { RandomizeDeployable } from "./isolated-deployment"; +import { ShowUtilities } from "./show-utilities"; interface Props { composeId: string; @@ -126,8 +127,7 @@ services:
- - +
+
+ +
+ +
+							
+						
+
+ + + + ); +}; diff --git a/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx b/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx index 59233acf..b1a00985 100644 --- a/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx @@ -1,14 +1,10 @@ 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, @@ -20,11 +16,6 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { - InputOTP, - InputOTPGroup, - InputOTPSlot, -} from "@/components/ui/input-otp"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -70,6 +61,7 @@ export const RandomizeCompose = ({ composeId }: Props) => { const suffix = form.watch("suffix"); useEffect(() => { + randomizeCompose(); if (data) { form.reset({ suffix: data?.suffix || "", @@ -110,126 +102,117 @@ export const RandomizeCompose = ({ composeId }: Props) => { }; return ( - - randomizeCompose()}> - - - - - Randomize Compose (Experimental) - - Use this in case you want to deploy the same compose file and you - have conflicts with some property like volumes, networks, etc. - - -
- - This will randomize the compose file and will add a suffix to the - property to avoid conflicts - -
    -
  • volumes
  • -
  • networks
  • -
  • services
  • -
  • configs
  • -
  • secrets
  • -
- - When you activate this option, we will include a env - `COMPOSE_PREFIX` variable to the compose file so you can use it in - your compose file. - -
- {isError && {error?.message}} -
- - {isError && ( -
- - - {error?.message} - -
- )} - -
-
- ( - - Suffix - - - - - - )} - /> - ( - -
- Apply Randomize - - Apply randomize to the compose file. - -
- - - -
- )} - /> -
- -
- - -
+
+ + Randomize Compose (Experimental) + + Use this in case you want to deploy the same compose file and you have + conflicts with some property like volumes, networks, etc. + + +
+ + This will randomize the compose file and will add a suffix to the + property to avoid conflicts + +
    +
  • volumes
  • +
  • networks
  • +
  • services
  • +
  • configs
  • +
  • secrets
  • +
+ + When you activate this option, we will include a env `COMPOSE_PREFIX` + variable to the compose file so you can use it in your compose file. + +
+ {isError && {error?.message}} + + + {isError && ( +
+ + + {error?.message} +
-
-							
+						
+ ( + + Suffix + + + + + + )} /> -
- - - -
+ ( + +
+ Apply Randomize + + Apply randomize to the compose file. + +
+ + + +
+ )} + /> + + +
+ + +
+ +
+						
+					
+ + + ); }; diff --git a/apps/dokploy/components/dashboard/compose/general/randomize-deployable.tsx b/apps/dokploy/components/dashboard/compose/general/randomize-deployable.tsx deleted file mode 100644 index db0c5242..00000000 --- a/apps/dokploy/components/dashboard/compose/general/randomize-deployable.tsx +++ /dev/null @@ -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; - -export const RandomizeDeployable = ({ composeId }: Props) => { - const utils = api.useUtils(); - const [compose, setCompose] = useState(""); - 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({ - 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 ( - - randomizeCompose()}> - - - - - Randomize Deployable (Experimental) - - Use this in case you want to deploy the same compose file twice. - - -
- - This will randomize the compose file and will add a suffix to the - property to avoid conflicts - -
    -
  • volumes
  • -
  • networks
  • -
- - 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{" "} - dokploy-network to any of the services by default. - -
- {isError && {error?.message}} -
- - {isError && ( -
- - - {error?.message} - -
- )} - -
-
- ( - -
- Apply Randomize ({data?.appName}) - - Apply randomize to the compose file. - -
- - - -
- )} - /> -
- -
- -
-
-
-							
-						
-
- -
-
- ); -}; diff --git a/apps/dokploy/components/dashboard/compose/general/show-utilities.tsx b/apps/dokploy/components/dashboard/compose/general/show-utilities.tsx new file mode 100644 index 00000000..6a3721e4 --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/general/show-utilities.tsx @@ -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 ( + + + + + + + Utilities + Modify the application data + + + + Isolated Deployment + Randomize Compose + + + + + + + + + + + ); +}; diff --git a/apps/dokploy/drizzle/0065_daily_zaladane.sql b/apps/dokploy/drizzle/0065_daily_zaladane.sql new file mode 100644 index 00000000..dc5c0d14 --- /dev/null +++ b/apps/dokploy/drizzle/0065_daily_zaladane.sql @@ -0,0 +1 @@ +ALTER TABLE "compose" RENAME COLUMN "deployable" TO "isolatedDeployment"; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0065_snapshot.json b/apps/dokploy/drizzle/meta/0065_snapshot.json new file mode 100644 index 00000000..28eef618 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0065_snapshot.json @@ -0,0 +1,4485 @@ +{ + "id": "1240ec96-1751-4de3-b64f-cef9cb716786", + "prevId": "c9ab7857-cc4c-475f-bd11-5f14f11017cc", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_adminId_admin_adminId_fk": { + "name": "project_adminId_admin_adminId_fk", + "tableFrom": "project", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_adminId_admin_adminId_fk": { + "name": "destination_adminId_admin_adminId_fk", + "tableFrom": "destination", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_adminId_admin_adminId_fk": { + "name": "certificate_adminId_admin_adminId_fk", + "tableFrom": "certificate", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_adminId_admin_adminId_fk": { + "name": "registry_adminId_admin_adminId_fk", + "tableFrom": "registry", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_adminId_admin_adminId_fk": { + "name": "notification_adminId_admin_adminId_fk", + "tableFrom": "notification", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_adminId_admin_adminId_fk": { + "name": "ssh-key_adminId_admin_adminId_fk", + "tableFrom": "ssh-key", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_adminId_admin_adminId_fk": { + "name": "git_provider_adminId_admin_adminId_fk", + "tableFrom": "git_provider", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_adminId_admin_adminId_fk": { + "name": "server_adminId_admin_adminId_fk", + "tableFrom": "server", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 72a61688..588d36eb 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -456,6 +456,13 @@ "when": 1738564387043, "tag": "0064_previous_agent_brand", "breakpoints": true + }, + { + "idx": 65, + "version": "7", + "when": 1739087857244, + "tag": "0065_daily_zaladane", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 9e42f935..380fc70f 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -1,22 +1,22 @@ import { slugify } from "@/lib/slug"; import { db } from "@/server/db"; import { - apiCreateCompose, - apiCreateComposeByTemplate, - apiDeleteCompose, - apiFetchServices, - apiFindCompose, - apiRandomizeCompose, - apiUpdateCompose, - compose, + apiCreateCompose, + apiCreateComposeByTemplate, + apiDeleteCompose, + apiFetchServices, + apiFindCompose, + apiRandomizeCompose, + apiUpdateCompose, + compose, } from "@/server/db/schema"; import { cleanQueuesByCompose, myQueue } from "@/server/queues/queueSetup"; import { templates } from "@/templates/templates"; import type { TemplatesKeys } from "@/templates/types/templates-data.type"; import { - generatePassword, - loadTemplateModule, - readTemplateComposeFile, + generatePassword, + loadTemplateModule, + readTemplateComposeFile, } from "@/templates/utils"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; @@ -28,443 +28,443 @@ import { createTRPCRouter, protectedProcedure } from "../trpc"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { deploy } from "@/server/utils/deploy"; import { - IS_CLOUD, - addDomainToCompose, - addNewService, - checkServiceAccess, - cloneCompose, - cloneComposeRemote, - createCommand, - createCompose, - createComposeByTemplate, - createDomain, - createMount, - findAdminById, - findComposeById, - findDomainsByComposeId, - findProjectById, - findServerById, - loadServices, - randomizeComposeFile, - randomizeDeployableComposeFile, - removeCompose, - removeComposeDirectory, - removeDeploymentsByComposeId, - startCompose, - stopCompose, - updateCompose, + IS_CLOUD, + addDomainToCompose, + addNewService, + checkServiceAccess, + cloneCompose, + cloneComposeRemote, + createCommand, + createCompose, + createComposeByTemplate, + createDomain, + createMount, + findAdminById, + findComposeById, + findDomainsByComposeId, + findProjectById, + findServerById, + loadServices, + randomizeComposeFile, + randomizeIsolatedDeploymentComposeFile, + removeCompose, + removeComposeDirectory, + removeDeploymentsByComposeId, + startCompose, + stopCompose, + updateCompose, } from "@dokploy/server"; export const composeRouter = createTRPCRouter({ - create: protectedProcedure - .input(apiCreateCompose) - .mutation(async ({ ctx, input }) => { - try { - if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); - } + create: protectedProcedure + .input(apiCreateCompose) + .mutation(async ({ ctx, input }) => { + try { + if (ctx.user.rol === "user") { + await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + } - if (IS_CLOUD && !input.serverId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You need to use a server to create a compose", - }); - } - const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this project", - }); - } - const newService = await createCompose(input); + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You need to use a server to create a compose", + }); + } + const project = await findProjectById(input.projectId); + if (project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this project", + }); + } + const newService = await createCompose(input); - if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newService.composeId); - } + if (ctx.user.rol === "user") { + await addNewService(ctx.user.authId, newService.composeId); + } - return newService; - } catch (error) { - throw error; - } - }), + return newService; + } catch (error) { + throw error; + } + }), - one: protectedProcedure - .input(apiFindCompose) - .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.composeId, "access"); - } + one: protectedProcedure + .input(apiFindCompose) + .query(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + await checkServiceAccess(ctx.user.authId, input.composeId, "access"); + } - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this compose", - }); - } - return compose; - }), + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this compose", + }); + } + return compose; + }), - update: protectedProcedure - .input(apiUpdateCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to update this compose", - }); - } - return updateCompose(input.composeId, input); - }), - delete: protectedProcedure - .input(apiDeleteCompose) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.composeId, "delete"); - } - const composeResult = await findComposeById(input.composeId); + update: protectedProcedure + .input(apiUpdateCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to update this compose", + }); + } + return updateCompose(input.composeId, input); + }), + delete: protectedProcedure + .input(apiDeleteCompose) + .mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + await checkServiceAccess(ctx.user.authId, input.composeId, "delete"); + } + const composeResult = await findComposeById(input.composeId); - if (composeResult.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to delete this compose", - }); - } - 4; + if (composeResult.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to delete this compose", + }); + } + 4; - const result = await db - .delete(compose) - .where(eq(compose.composeId, input.composeId)) - .returning(); + const result = await db + .delete(compose) + .where(eq(compose.composeId, input.composeId)) + .returning(); - const cleanupOperations = [ - async () => await removeCompose(composeResult, input.deleteVolumes), - async () => await removeDeploymentsByComposeId(composeResult), - async () => await removeComposeDirectory(composeResult.appName), - ]; + const cleanupOperations = [ + async () => await removeCompose(composeResult, input.deleteVolumes), + async () => await removeDeploymentsByComposeId(composeResult), + async () => await removeComposeDirectory(composeResult.appName), + ]; - for (const operation of cleanupOperations) { - try { - await operation(); - } catch (error) {} - } + for (const operation of cleanupOperations) { + try { + await operation(); + } catch (error) {} + } - return result[0]; - }), - cleanQueues: protectedProcedure - .input(apiFindCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to clean this compose", - }); - } - await cleanQueuesByCompose(input.composeId); - }), + return result[0]; + }), + cleanQueues: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to clean this compose", + }); + } + await cleanQueuesByCompose(input.composeId); + }), - loadServices: protectedProcedure - .input(apiFetchServices) - .query(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to load this compose", - }); - } - return await loadServices(input.composeId, input.type); - }), - fetchSourceType: protectedProcedure - .input(apiFindCompose) - .mutation(async ({ input, ctx }) => { - try { - const compose = await findComposeById(input.composeId); + loadServices: protectedProcedure + .input(apiFetchServices) + .query(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to load this compose", + }); + } + return await loadServices(input.composeId, input.type); + }), + fetchSourceType: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + try { + const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to fetch this compose", - }); - } - if (compose.serverId) { - await cloneComposeRemote(compose); - } else { - await cloneCompose(compose); - } - return compose.sourceType; - } catch (err) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error fetching source type", - cause: err, - }); - } - }), + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to fetch this compose", + }); + } + if (compose.serverId) { + await cloneComposeRemote(compose); + } else { + await cloneCompose(compose); + } + return compose.sourceType; + } catch (err) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error fetching source type", + cause: err, + }); + } + }), - randomizeCompose: protectedProcedure - .input(apiRandomizeCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to randomize this compose", - }); - } - return await randomizeComposeFile(input.composeId, input.suffix); - }), - randomizeDeployableCompose: protectedProcedure - .input(apiRandomizeCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to randomize this compose", - }); - } - return await randomizeDeployableComposeFile( - input.composeId, - input.suffix, - ); - }), - getConvertedCompose: protectedProcedure - .input(apiFindCompose) - .query(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to get this compose", - }); - } - const domains = await findDomainsByComposeId(input.composeId); - const composeFile = await addDomainToCompose(compose, domains); - return dump(composeFile, { - lineWidth: 1000, - }); - }), + randomizeCompose: protectedProcedure + .input(apiRandomizeCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to randomize this compose", + }); + } + return await randomizeComposeFile(input.composeId, input.suffix); + }), + isolatedDeployment: protectedProcedure + .input(apiRandomizeCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to randomize this compose", + }); + } + return await randomizeIsolatedDeploymentComposeFile( + input.composeId, + input.suffix + ); + }), + getConvertedCompose: protectedProcedure + .input(apiFindCompose) + .query(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to get this compose", + }); + } + const domains = await findDomainsByComposeId(input.composeId); + const composeFile = await addDomainToCompose(compose, domains); + return dump(composeFile, { + lineWidth: 1000, + }); + }), - deploy: protectedProcedure - .input(apiFindCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); + deploy: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to deploy this compose", - }); - } - const jobData: DeploymentJob = { - composeId: input.composeId, - titleLog: "Manual deployment", - type: "deploy", - applicationType: "compose", - descriptionLog: "", - server: !!compose.serverId, - }; + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to deploy this compose", + }); + } + const jobData: DeploymentJob = { + composeId: input.composeId, + titleLog: "Manual deployment", + type: "deploy", + applicationType: "compose", + descriptionLog: "", + server: !!compose.serverId, + }; - if (IS_CLOUD && compose.serverId) { - jobData.serverId = compose.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); - }), - redeploy: protectedProcedure - .input(apiFindCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to redeploy this compose", - }); - } - const jobData: DeploymentJob = { - composeId: input.composeId, - titleLog: "Rebuild deployment", - type: "redeploy", - applicationType: "compose", - descriptionLog: "", - server: !!compose.serverId, - }; - if (IS_CLOUD && compose.serverId) { - jobData.serverId = compose.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); - }), - stop: protectedProcedure - .input(apiFindCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to stop this compose", - }); - } - await stopCompose(input.composeId); + if (IS_CLOUD && compose.serverId) { + jobData.serverId = compose.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + } + ); + }), + redeploy: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to redeploy this compose", + }); + } + const jobData: DeploymentJob = { + composeId: input.composeId, + titleLog: "Rebuild deployment", + type: "redeploy", + applicationType: "compose", + descriptionLog: "", + server: !!compose.serverId, + }; + if (IS_CLOUD && compose.serverId) { + jobData.serverId = compose.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + } + ); + }), + stop: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to stop this compose", + }); + } + await stopCompose(input.composeId); - return true; - }), - start: protectedProcedure - .input(apiFindCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to stop this compose", - }); - } - await startCompose(input.composeId); + return true; + }), + start: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to stop this compose", + }); + } + await startCompose(input.composeId); - return true; - }), - getDefaultCommand: protectedProcedure - .input(apiFindCompose) - .query(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); + return true; + }), + getDefaultCommand: protectedProcedure + .input(apiFindCompose) + .query(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to get this compose", - }); - } - const command = createCommand(compose); - return `docker ${command}`; - }), - refreshToken: protectedProcedure - .input(apiFindCompose) - .mutation(async ({ input, ctx }) => { - const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to refresh this compose", - }); - } - await updateCompose(input.composeId, { - refreshToken: nanoid(), - }); - return true; - }), - deployTemplate: protectedProcedure - .input(apiCreateComposeByTemplate) - .mutation(async ({ ctx, input }) => { - if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); - } + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to get this compose", + }); + } + const command = createCommand(compose); + return `docker ${command}`; + }), + refreshToken: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to refresh this compose", + }); + } + await updateCompose(input.composeId, { + refreshToken: nanoid(), + }); + return true; + }), + deployTemplate: protectedProcedure + .input(apiCreateComposeByTemplate) + .mutation(async ({ ctx, input }) => { + if (ctx.user.rol === "user") { + await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + } - if (IS_CLOUD && !input.serverId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You need to use a server to create a compose", - }); - } + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You need to use a server to create a compose", + }); + } - const composeFile = await readTemplateComposeFile(input.id); + const composeFile = await readTemplateComposeFile(input.id); - const generate = await loadTemplateModule(input.id as TemplatesKeys); + const generate = await loadTemplateModule(input.id as TemplatesKeys); - const admin = await findAdminById(ctx.user.adminId); - let serverIp = admin.serverIp || "127.0.0.1"; + const admin = await findAdminById(ctx.user.adminId); + let serverIp = admin.serverIp || "127.0.0.1"; - const project = await findProjectById(input.projectId); + const project = await findProjectById(input.projectId); - if (input.serverId) { - const server = await findServerById(input.serverId); - serverIp = server.ipAddress; - } else if (process.env.NODE_ENV === "development") { - serverIp = "127.0.0.1"; - } - const projectName = slugify(`${project.name} ${input.id}`); - const { envs, mounts, domains } = generate({ - serverIp: serverIp || "", - projectName: projectName, - }); + if (input.serverId) { + const server = await findServerById(input.serverId); + serverIp = server.ipAddress; + } else if (process.env.NODE_ENV === "development") { + serverIp = "127.0.0.1"; + } + const projectName = slugify(`${project.name} ${input.id}`); + const { envs, mounts, domains } = generate({ + serverIp: serverIp || "", + projectName: projectName, + }); - const compose = await createComposeByTemplate({ - ...input, - composeFile: composeFile, - env: envs?.join("\n"), - serverId: input.serverId, - name: input.id, - sourceType: "raw", - appName: `${projectName}-${generatePassword(6)}`, - }); + const compose = await createComposeByTemplate({ + ...input, + composeFile: composeFile, + env: envs?.join("\n"), + serverId: input.serverId, + name: input.id, + sourceType: "raw", + appName: `${projectName}-${generatePassword(6)}`, + }); - if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, compose.composeId); - } + if (ctx.user.rol === "user") { + await addNewService(ctx.user.authId, compose.composeId); + } - if (mounts && mounts?.length > 0) { - for (const mount of mounts) { - await createMount({ - filePath: mount.filePath, - mountPath: "", - content: mount.content, - serviceId: compose.composeId, - serviceType: "compose", - type: "file", - }); - } - } + if (mounts && mounts?.length > 0) { + for (const mount of mounts) { + await createMount({ + filePath: mount.filePath, + mountPath: "", + content: mount.content, + serviceId: compose.composeId, + serviceType: "compose", + type: "file", + }); + } + } - if (domains && domains?.length > 0) { - for (const domain of domains) { - await createDomain({ - ...domain, - domainType: "compose", - certificateType: "none", - composeId: compose.composeId, - }); - } - } + if (domains && domains?.length > 0) { + for (const domain of domains) { + await createDomain({ + ...domain, + domainType: "compose", + certificateType: "none", + composeId: compose.composeId, + }); + } + } - return null; - }), + return null; + }), - templates: protectedProcedure.query(async () => { - const templatesData = templates.map((t) => ({ - name: t.name, - description: t.description, - id: t.id, - links: t.links, - tags: t.tags, - logo: t.logo, - version: t.version, - })); + templates: protectedProcedure.query(async () => { + const templatesData = templates.map((t) => ({ + name: t.name, + description: t.description, + id: t.id, + links: t.links, + tags: t.tags, + logo: t.logo, + version: t.version, + })); - return templatesData; - }), + return templatesData; + }), - getTags: protectedProcedure.query(async ({ input }) => { - const allTags = templates.flatMap((template) => template.tags); - const uniqueTags = _.uniq(allTags); - return uniqueTags; - }), + getTags: protectedProcedure.query(async ({ input }) => { + const allTags = templates.flatMap((template) => template.tags); + const uniqueTags = _.uniq(allTags); + return uniqueTags; + }), }); diff --git a/packages/server/src/db/schema/compose.ts b/packages/server/src/db/schema/compose.ts index 8efcc314..e0f13c29 100644 --- a/packages/server/src/db/schema/compose.ts +++ b/packages/server/src/db/schema/compose.ts @@ -16,170 +16,170 @@ import { sshKeys } from "./ssh-key"; import { generateAppName } from "./utils"; export const sourceTypeCompose = pgEnum("sourceTypeCompose", [ - "git", - "github", - "gitlab", - "bitbucket", - "raw", + "git", + "github", + "gitlab", + "bitbucket", + "raw", ]); export const composeType = pgEnum("composeType", ["docker-compose", "stack"]); export const compose = pgTable("compose", { - composeId: text("composeId") - .notNull() - .primaryKey() - .$defaultFn(() => nanoid()), - name: text("name").notNull(), - appName: text("appName") - .notNull() - .$defaultFn(() => generateAppName("compose")), - description: text("description"), - env: text("env"), - composeFile: text("composeFile").notNull().default(""), - refreshToken: text("refreshToken").$defaultFn(() => nanoid()), - sourceType: sourceTypeCompose("sourceType").notNull().default("github"), - composeType: composeType("composeType").notNull().default("docker-compose"), - // Github - repository: text("repository"), - owner: text("owner"), - branch: text("branch"), - autoDeploy: boolean("autoDeploy").$defaultFn(() => true), - // Gitlab - gitlabProjectId: integer("gitlabProjectId"), - gitlabRepository: text("gitlabRepository"), - gitlabOwner: text("gitlabOwner"), - gitlabBranch: text("gitlabBranch"), - gitlabPathNamespace: text("gitlabPathNamespace"), - // Bitbucket - bitbucketRepository: text("bitbucketRepository"), - bitbucketOwner: text("bitbucketOwner"), - bitbucketBranch: text("bitbucketBranch"), - // Git - customGitUrl: text("customGitUrl"), - customGitBranch: text("customGitBranch"), - customGitSSHKeyId: text("customGitSSHKeyId").references( - () => sshKeys.sshKeyId, - { - onDelete: "set null", - }, - ), - command: text("command").notNull().default(""), - // - composePath: text("composePath").notNull().default("./docker-compose.yml"), - suffix: text("suffix").notNull().default(""), - randomize: boolean("randomize").notNull().default(false), - deployable: boolean("deployable").notNull().default(false), - composeStatus: applicationStatus("composeStatus").notNull().default("idle"), - projectId: text("projectId") - .notNull() - .references(() => projects.projectId, { onDelete: "cascade" }), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), + composeId: text("composeId") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + name: text("name").notNull(), + appName: text("appName") + .notNull() + .$defaultFn(() => generateAppName("compose")), + description: text("description"), + env: text("env"), + composeFile: text("composeFile").notNull().default(""), + refreshToken: text("refreshToken").$defaultFn(() => nanoid()), + sourceType: sourceTypeCompose("sourceType").notNull().default("github"), + composeType: composeType("composeType").notNull().default("docker-compose"), + // Github + repository: text("repository"), + owner: text("owner"), + branch: text("branch"), + autoDeploy: boolean("autoDeploy").$defaultFn(() => true), + // Gitlab + gitlabProjectId: integer("gitlabProjectId"), + gitlabRepository: text("gitlabRepository"), + gitlabOwner: text("gitlabOwner"), + gitlabBranch: text("gitlabBranch"), + gitlabPathNamespace: text("gitlabPathNamespace"), + // Bitbucket + bitbucketRepository: text("bitbucketRepository"), + bitbucketOwner: text("bitbucketOwner"), + bitbucketBranch: text("bitbucketBranch"), + // Git + customGitUrl: text("customGitUrl"), + customGitBranch: text("customGitBranch"), + customGitSSHKeyId: text("customGitSSHKeyId").references( + () => sshKeys.sshKeyId, + { + onDelete: "set null", + } + ), + command: text("command").notNull().default(""), + // + composePath: text("composePath").notNull().default("./docker-compose.yml"), + suffix: text("suffix").notNull().default(""), + randomize: boolean("randomize").notNull().default(false), + isolatedDeployment: boolean("isolatedDeployment").notNull().default(false), + composeStatus: applicationStatus("composeStatus").notNull().default("idle"), + projectId: text("projectId") + .notNull() + .references(() => projects.projectId, { onDelete: "cascade" }), + createdAt: text("createdAt") + .notNull() + .$defaultFn(() => new Date().toISOString()), - githubId: text("githubId").references(() => github.githubId, { - onDelete: "set null", - }), - gitlabId: text("gitlabId").references(() => gitlab.gitlabId, { - onDelete: "set null", - }), - bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, { - onDelete: "set null", - }), - serverId: text("serverId").references(() => server.serverId, { - onDelete: "cascade", - }), + githubId: text("githubId").references(() => github.githubId, { + onDelete: "set null", + }), + gitlabId: text("gitlabId").references(() => gitlab.gitlabId, { + onDelete: "set null", + }), + bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, { + onDelete: "set null", + }), + serverId: text("serverId").references(() => server.serverId, { + onDelete: "cascade", + }), }); export const composeRelations = relations(compose, ({ one, many }) => ({ - project: one(projects, { - fields: [compose.projectId], - references: [projects.projectId], - }), - deployments: many(deployments), - mounts: many(mounts), - customGitSSHKey: one(sshKeys, { - fields: [compose.customGitSSHKeyId], - references: [sshKeys.sshKeyId], - }), - domains: many(domains), - github: one(github, { - fields: [compose.githubId], - references: [github.githubId], - }), - gitlab: one(gitlab, { - fields: [compose.gitlabId], - references: [gitlab.gitlabId], - }), - bitbucket: one(bitbucket, { - fields: [compose.bitbucketId], - references: [bitbucket.bitbucketId], - }), - server: one(server, { - fields: [compose.serverId], - references: [server.serverId], - }), + project: one(projects, { + fields: [compose.projectId], + references: [projects.projectId], + }), + deployments: many(deployments), + mounts: many(mounts), + customGitSSHKey: one(sshKeys, { + fields: [compose.customGitSSHKeyId], + references: [sshKeys.sshKeyId], + }), + domains: many(domains), + github: one(github, { + fields: [compose.githubId], + references: [github.githubId], + }), + gitlab: one(gitlab, { + fields: [compose.gitlabId], + references: [gitlab.gitlabId], + }), + bitbucket: one(bitbucket, { + fields: [compose.bitbucketId], + references: [bitbucket.bitbucketId], + }), + server: one(server, { + fields: [compose.serverId], + references: [server.serverId], + }), })); const createSchema = createInsertSchema(compose, { - name: z.string().min(1), - description: z.string(), - env: z.string().optional(), - composeFile: z.string().min(1), - projectId: z.string(), - customGitSSHKeyId: z.string().optional(), - command: z.string().optional(), - composePath: z.string().min(1), - composeType: z.enum(["docker-compose", "stack"]).optional(), + name: z.string().min(1), + description: z.string(), + env: z.string().optional(), + composeFile: z.string().min(1), + projectId: z.string(), + customGitSSHKeyId: z.string().optional(), + command: z.string().optional(), + composePath: z.string().min(1), + composeType: z.enum(["docker-compose", "stack"]).optional(), }); export const apiCreateCompose = createSchema.pick({ - name: true, - description: true, - projectId: true, - composeType: true, - appName: true, - serverId: true, + name: true, + description: true, + projectId: true, + composeType: true, + appName: true, + serverId: true, }); export const apiCreateComposeByTemplate = createSchema - .pick({ - projectId: true, - }) - .extend({ - id: z.string().min(1), - serverId: z.string().optional(), - }); + .pick({ + projectId: true, + }) + .extend({ + id: z.string().min(1), + serverId: z.string().optional(), + }); export const apiFindCompose = z.object({ - composeId: z.string().min(1), + composeId: z.string().min(1), }); export const apiDeleteCompose = z.object({ - composeId: z.string().min(1), - deleteVolumes: z.boolean(), + composeId: z.string().min(1), + deleteVolumes: z.boolean(), }); export const apiFetchServices = z.object({ - composeId: z.string().min(1), - type: z.enum(["fetch", "cache"]).optional().default("cache"), + composeId: z.string().min(1), + type: z.enum(["fetch", "cache"]).optional().default("cache"), }); export const apiUpdateCompose = createSchema - .partial() - .extend({ - composeId: z.string(), - composeFile: z.string().optional(), - command: z.string().optional(), - }) - .omit({ serverId: true }); + .partial() + .extend({ + composeId: z.string(), + composeFile: z.string().optional(), + command: z.string().optional(), + }) + .omit({ serverId: true }); export const apiRandomizeCompose = createSchema - .pick({ - composeId: true, - }) - .extend({ - suffix: z.string().optional(), - composeId: z.string().min(1), - }); + .pick({ + composeId: true, + }) + .extend({ + suffix: z.string().optional(), + composeId: z.string().min(1), + }); diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts index f5cedfd7..648b598f 100644 --- a/packages/server/src/utils/builders/compose.ts +++ b/packages/server/src/utils/builders/compose.ts @@ -1,117 +1,117 @@ import { - createWriteStream, - existsSync, - mkdirSync, - readFileSync, - writeFileSync, + createWriteStream, + existsSync, + mkdirSync, + readFileSync, + writeFileSync, } from "node:fs"; import { dirname, join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { InferResultType } from "@dokploy/server/types/with"; import boxen from "boxen"; import { - writeDomainsToCompose, - writeDomainsToComposeRemote, + writeDomainsToCompose, + writeDomainsToComposeRemote, } from "../docker/domain"; import { - encodeBase64, - getEnviromentVariablesObject, - prepareEnvironmentVariables, + encodeBase64, + getEnviromentVariablesObject, + prepareEnvironmentVariables, } from "../docker/utils"; import { execAsync, execAsyncRemote } from "../process/execAsync"; import { spawnAsync } from "../process/spawnAsync"; export type ComposeNested = InferResultType< - "compose", - { project: true; mounts: true; domains: true } + "compose", + { project: true; mounts: true; domains: true } >; export const buildCompose = async (compose: ComposeNested, logPath: string) => { - const writeStream = createWriteStream(logPath, { flags: "a" }); - const { sourceType, appName, mounts, composeType, domains } = compose; - try { - const { COMPOSE_PATH } = paths(); - const command = createCommand(compose); - await writeDomainsToCompose(compose, domains); - createEnvFile(compose); + const writeStream = createWriteStream(logPath, { flags: "a" }); + const { sourceType, appName, mounts, composeType, domains } = compose; + try { + const { COMPOSE_PATH } = paths(); + const command = createCommand(compose); + await writeDomainsToCompose(compose, domains); + createEnvFile(compose); - if (compose.deployable) { - await execAsync( - `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}`, - ); - } + if (compose.isolatedDeployment) { + await execAsync( + `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` + ); + } - const logContent = ` + const logContent = ` App Name: ${appName} Build Compose 🐳 Detected: ${mounts.length} mounts 📂 Command: docker ${command} Source Type: docker ${sourceType} ✅ Compose Type: ${composeType} ✅`; - const logBox = boxen(logContent, { - padding: { - left: 1, - right: 1, - bottom: 1, - }, - width: 80, - borderStyle: "double", - }); - writeStream.write(`\n${logBox}\n`); - const projectPath = join(COMPOSE_PATH, compose.appName, "code"); + const logBox = boxen(logContent, { + padding: { + left: 1, + right: 1, + bottom: 1, + }, + width: 80, + borderStyle: "double", + }); + writeStream.write(`\n${logBox}\n`); + const projectPath = join(COMPOSE_PATH, compose.appName, "code"); - await spawnAsync( - "docker", - [...command.split(" ")], - (data) => { - if (writeStream.writable) { - writeStream.write(data.toString()); - } - }, - { - cwd: projectPath, - env: { - NODE_ENV: process.env.NODE_ENV, - PATH: process.env.PATH, - ...(composeType === "stack" && { - ...getEnviromentVariablesObject(compose.env, compose.project.env), - }), - }, - }, - ); + await spawnAsync( + "docker", + [...command.split(" ")], + (data) => { + if (writeStream.writable) { + writeStream.write(data.toString()); + } + }, + { + cwd: projectPath, + env: { + NODE_ENV: process.env.NODE_ENV, + PATH: process.env.PATH, + ...(composeType === "stack" && { + ...getEnviromentVariablesObject(compose.env, compose.project.env), + }), + }, + } + ); - if (compose.deployable) { - await execAsync( - `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`, - ); - } + if (compose.isolatedDeployment) { + await execAsync( + `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` + ); + } - writeStream.write("Docker Compose Deployed: ✅"); - } catch (error) { - writeStream.write(`Error ❌ ${(error as Error).message}`); - throw error; - } finally { - writeStream.end(); - } + writeStream.write("Docker Compose Deployed: ✅"); + } catch (error) { + writeStream.write(`Error ❌ ${(error as Error).message}`); + throw error; + } finally { + writeStream.end(); + } }; export const getBuildComposeCommand = async ( - compose: ComposeNested, - logPath: string, + compose: ComposeNested, + logPath: string ) => { - const { COMPOSE_PATH } = paths(true); - const { sourceType, appName, mounts, composeType, domains, composePath } = - compose; - const command = createCommand(compose); - const envCommand = getCreateEnvFileCommand(compose); - const projectPath = join(COMPOSE_PATH, compose.appName, "code"); - const exportEnvCommand = getExportEnvCommand(compose); + const { COMPOSE_PATH } = paths(true); + const { sourceType, appName, mounts, composeType, domains, composePath } = + compose; + const command = createCommand(compose); + const envCommand = getCreateEnvFileCommand(compose); + const projectPath = join(COMPOSE_PATH, compose.appName, "code"); + const exportEnvCommand = getExportEnvCommand(compose); - const newCompose = await writeDomainsToComposeRemote( - compose, - domains, - logPath, - ); - const logContent = ` + const newCompose = await writeDomainsToComposeRemote( + compose, + domains, + logPath + ); + const logContent = ` App Name: ${appName} Build Compose 🐳 Detected: ${mounts.length} mounts 📂 @@ -119,17 +119,17 @@ Command: docker ${command} Source Type: docker ${sourceType} ✅ Compose Type: ${composeType} ✅`; - const logBox = boxen(logContent, { - padding: { - left: 1, - right: 1, - bottom: 1, - }, - width: 80, - borderStyle: "double", - }); + const logBox = boxen(logContent, { + padding: { + left: 1, + right: 1, + bottom: 1, + }, + width: 80, + borderStyle: "double", + }); - const bashCommand = ` + const bashCommand = ` set -e { echo "${logBox}" >> "${logPath}" @@ -141,9 +141,9 @@ Compose Type: ${composeType} ✅`; cd "${projectPath}"; ${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; } - ${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}" } || { @@ -152,106 +152,106 @@ Compose Type: ${composeType} ✅`; } `; - return await execAsyncRemote(compose.serverId, bashCommand); + return await execAsyncRemote(compose.serverId, bashCommand); }; const sanitizeCommand = (command: string) => { - const sanitizedCommand = command.trim(); + const sanitizedCommand = command.trim(); - const parts = sanitizedCommand.split(/\s+/); + const parts = sanitizedCommand.split(/\s+/); - const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1")); + const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1")); - return restCommand.join(" "); + return restCommand.join(" "); }; export const createCommand = (compose: ComposeNested) => { - const { composeType, appName, sourceType } = compose; - if (compose.command) { - return `${sanitizeCommand(compose.command)}`; - } + const { composeType, appName, sourceType } = compose; + if (compose.command) { + return `${sanitizeCommand(compose.command)}`; + } - const path = - sourceType === "raw" ? "docker-compose.yml" : compose.composePath; - let command = ""; + const path = + sourceType === "raw" ? "docker-compose.yml" : compose.composePath; + let command = ""; - if (composeType === "docker-compose") { - command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`; - } else if (composeType === "stack") { - command = `stack deploy -c ${path} ${appName} --prune`; - } + if (composeType === "docker-compose") { + command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`; + } else if (composeType === "stack") { + command = `stack deploy -c ${path} ${appName} --prune`; + } - return command; + return command; }; const createEnvFile = (compose: ComposeNested) => { - const { COMPOSE_PATH } = paths(); - const { env, composePath, appName } = compose; - const composeFilePath = - join(COMPOSE_PATH, appName, "code", composePath) || - join(COMPOSE_PATH, appName, "code", "docker-compose.yml"); + const { COMPOSE_PATH } = paths(); + const { env, composePath, appName } = compose; + const composeFilePath = + join(COMPOSE_PATH, appName, "code", composePath) || + join(COMPOSE_PATH, appName, "code", "docker-compose.yml"); - const envFilePath = join(dirname(composeFilePath), ".env"); - let envContent = env || ""; - if (!envContent.includes("DOCKER_CONFIG")) { - envContent += "\nDOCKER_CONFIG=/root/.docker/config.json"; - } + const envFilePath = join(dirname(composeFilePath), ".env"); + let envContent = env || ""; + if (!envContent.includes("DOCKER_CONFIG")) { + envContent += "\nDOCKER_CONFIG=/root/.docker/config.json"; + } - if (compose.randomize) { - envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`; - } + if (compose.randomize) { + envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`; + } - const envFileContent = prepareEnvironmentVariables( - envContent, - compose.project.env, - ).join("\n"); + const envFileContent = prepareEnvironmentVariables( + envContent, + compose.project.env + ).join("\n"); - if (!existsSync(dirname(envFilePath))) { - mkdirSync(dirname(envFilePath), { recursive: true }); - } - writeFileSync(envFilePath, envFileContent); + if (!existsSync(dirname(envFilePath))) { + mkdirSync(dirname(envFilePath), { recursive: true }); + } + writeFileSync(envFilePath, envFileContent); }; export const getCreateEnvFileCommand = (compose: ComposeNested) => { - const { COMPOSE_PATH } = paths(true); - const { env, composePath, appName } = compose; - const composeFilePath = - join(COMPOSE_PATH, appName, "code", composePath) || - join(COMPOSE_PATH, appName, "code", "docker-compose.yml"); + const { COMPOSE_PATH } = paths(true); + const { env, composePath, appName } = compose; + const composeFilePath = + join(COMPOSE_PATH, appName, "code", composePath) || + join(COMPOSE_PATH, appName, "code", "docker-compose.yml"); - const envFilePath = join(dirname(composeFilePath), ".env"); + const envFilePath = join(dirname(composeFilePath), ".env"); - let envContent = env || ""; - if (!envContent.includes("DOCKER_CONFIG")) { - envContent += "\nDOCKER_CONFIG=/root/.docker/config.json"; - } + let envContent = env || ""; + if (!envContent.includes("DOCKER_CONFIG")) { + envContent += "\nDOCKER_CONFIG=/root/.docker/config.json"; + } - if (compose.randomize) { - envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`; - } + if (compose.randomize) { + envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`; + } - const envFileContent = prepareEnvironmentVariables( - envContent, - compose.project.env, - ).join("\n"); + const envFileContent = prepareEnvironmentVariables( + envContent, + compose.project.env + ).join("\n"); - const encodedContent = encodeBase64(envFileContent); - return ` + const encodedContent = encodeBase64(envFileContent); + return ` touch ${envFilePath}; echo "${encodedContent}" | base64 -d > "${envFilePath}"; `; }; const getExportEnvCommand = (compose: ComposeNested) => { - if (compose.composeType !== "stack") return ""; + if (compose.composeType !== "stack") return ""; - const envVars = getEnviromentVariablesObject( - compose.env, - compose.project.env, - ); - const exports = Object.entries(envVars) - .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`) - .join("\n"); + const envVars = getEnviromentVariablesObject( + compose.env, + compose.project.env + ); + const exports = Object.entries(envVars) + .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`) + .join("\n"); - return exports ? `\n# Export environment variables\n${exports}\n` : ""; + return exports ? `\n# Export environment variables\n${exports}\n` : ""; }; diff --git a/packages/server/src/utils/docker/collision.ts b/packages/server/src/utils/docker/collision.ts index b905d486..e0fa725a 100644 --- a/packages/server/src/utils/docker/collision.ts +++ b/packages/server/src/utils/docker/collision.ts @@ -6,41 +6,41 @@ import { addSuffixToAllVolumes } from "./compose/volume"; import type { ComposeSpecification } from "./types"; export const addAppNameToPreventCollision = ( - composeData: ComposeSpecification, - appName: string, + composeData: ComposeSpecification, + appName: string ): ComposeSpecification => { - let updatedComposeData = { ...composeData }; + let updatedComposeData = { ...composeData }; - updatedComposeData = addAppNameToAllServiceNames(updatedComposeData, appName); - updatedComposeData = addSuffixToAllVolumes(updatedComposeData, appName); - return updatedComposeData; + updatedComposeData = addAppNameToAllServiceNames(updatedComposeData, appName); + updatedComposeData = addSuffixToAllVolumes(updatedComposeData, appName); + return updatedComposeData; }; -export const randomizeDeployableComposeFile = async ( - composeId: string, - suffix?: string, +export const randomizeIsolatedDeploymentComposeFile = async ( + composeId: string, + suffix?: string ) => { - const compose = await findComposeById(composeId); - const composeFile = compose.composeFile; - const composeData = load(composeFile) as ComposeSpecification; + const compose = await findComposeById(composeId); + const composeFile = compose.composeFile; + const composeData = load(composeFile) as ComposeSpecification; - const randomSuffix = suffix || compose.appName || generateRandomHash(); + const randomSuffix = suffix || compose.appName || generateRandomHash(); - const newComposeFile = addAppNameToPreventCollision( - composeData, - randomSuffix, - ); + const newComposeFile = addAppNameToPreventCollision( + composeData, + randomSuffix + ); - return dump(newComposeFile); + return dump(newComposeFile); }; export const randomizeDeployableSpecificationFile = ( - composeSpec: ComposeSpecification, - suffix?: string, + composeSpec: ComposeSpecification, + suffix?: string ) => { - if (!suffix) { - return composeSpec; - } - const newComposeFile = addAppNameToPreventCollision(composeSpec, suffix); - return newComposeFile; + if (!suffix) { + return composeSpec; + } + const newComposeFile = addAppNameToPreventCollision(composeSpec, suffix); + return newComposeFile; }; diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts index 6846c830..f9eade7d 100644 --- a/packages/server/src/utils/docker/domain.ts +++ b/packages/server/src/utils/docker/domain.ts @@ -7,346 +7,346 @@ import type { Domain } from "@dokploy/server/services/domain"; import { dump, load } from "js-yaml"; import { execAsyncRemote } from "../process/execAsync"; import { - cloneRawBitbucketRepository, - cloneRawBitbucketRepositoryRemote, + cloneRawBitbucketRepository, + cloneRawBitbucketRepositoryRemote, } from "../providers/bitbucket"; import { - cloneGitRawRepository, - cloneRawGitRepositoryRemote, + cloneGitRawRepository, + cloneRawGitRepositoryRemote, } from "../providers/git"; import { - cloneRawGithubRepository, - cloneRawGithubRepositoryRemote, + cloneRawGithubRepository, + cloneRawGithubRepositoryRemote, } from "../providers/github"; import { - cloneRawGitlabRepository, - cloneRawGitlabRepositoryRemote, + cloneRawGitlabRepository, + cloneRawGitlabRepositoryRemote, } from "../providers/gitlab"; import { - createComposeFileRaw, - createComposeFileRawRemote, + createComposeFileRaw, + createComposeFileRawRemote, } from "../providers/raw"; import { randomizeDeployableSpecificationFile } from "./collision"; import { randomizeSpecificationFile } from "./compose"; import type { - ComposeSpecification, - DefinitionsService, - PropertiesNetworks, + ComposeSpecification, + DefinitionsService, + PropertiesNetworks, } from "./types"; import { encodeBase64 } from "./utils"; export const cloneCompose = async (compose: Compose) => { - if (compose.sourceType === "github") { - await cloneRawGithubRepository(compose); - } else if (compose.sourceType === "gitlab") { - await cloneRawGitlabRepository(compose); - } else if (compose.sourceType === "bitbucket") { - await cloneRawBitbucketRepository(compose); - } else if (compose.sourceType === "git") { - await cloneGitRawRepository(compose); - } else if (compose.sourceType === "raw") { - await createComposeFileRaw(compose); - } + if (compose.sourceType === "github") { + await cloneRawGithubRepository(compose); + } else if (compose.sourceType === "gitlab") { + await cloneRawGitlabRepository(compose); + } else if (compose.sourceType === "bitbucket") { + await cloneRawBitbucketRepository(compose); + } else if (compose.sourceType === "git") { + await cloneGitRawRepository(compose); + } else if (compose.sourceType === "raw") { + await createComposeFileRaw(compose); + } }; export const cloneComposeRemote = async (compose: Compose) => { - if (compose.sourceType === "github") { - await cloneRawGithubRepositoryRemote(compose); - } else if (compose.sourceType === "gitlab") { - await cloneRawGitlabRepositoryRemote(compose); - } else if (compose.sourceType === "bitbucket") { - await cloneRawBitbucketRepositoryRemote(compose); - } else if (compose.sourceType === "git") { - await cloneRawGitRepositoryRemote(compose); - } else if (compose.sourceType === "raw") { - await createComposeFileRawRemote(compose); - } + if (compose.sourceType === "github") { + await cloneRawGithubRepositoryRemote(compose); + } else if (compose.sourceType === "gitlab") { + await cloneRawGitlabRepositoryRemote(compose); + } else if (compose.sourceType === "bitbucket") { + await cloneRawBitbucketRepositoryRemote(compose); + } else if (compose.sourceType === "git") { + await cloneRawGitRepositoryRemote(compose); + } else if (compose.sourceType === "raw") { + await createComposeFileRawRemote(compose); + } }; export const getComposePath = (compose: Compose) => { - const { COMPOSE_PATH } = paths(!!compose.serverId); - const { appName, sourceType, composePath } = compose; - let path = ""; + const { COMPOSE_PATH } = paths(!!compose.serverId); + const { appName, sourceType, composePath } = compose; + let path = ""; - if (sourceType === "raw") { - path = "docker-compose.yml"; - } else { - path = composePath; - } + if (sourceType === "raw") { + path = "docker-compose.yml"; + } else { + path = composePath; + } - return join(COMPOSE_PATH, appName, "code", path); + return join(COMPOSE_PATH, appName, "code", path); }; export const loadDockerCompose = async ( - compose: Compose, + compose: Compose ): Promise => { - const path = getComposePath(compose); + const path = getComposePath(compose); - if (existsSync(path)) { - const yamlStr = readFileSync(path, "utf8"); - const parsedConfig = load(yamlStr) as ComposeSpecification; - return parsedConfig; - } - return null; + if (existsSync(path)) { + const yamlStr = readFileSync(path, "utf8"); + const parsedConfig = load(yamlStr) as ComposeSpecification; + return parsedConfig; + } + return null; }; export const loadDockerComposeRemote = async ( - compose: Compose, + compose: Compose ): Promise => { - const path = getComposePath(compose); - try { - if (!compose.serverId) { - return null; - } - const { stdout, stderr } = await execAsyncRemote( - compose.serverId, - `cat ${path}`, - ); + const path = getComposePath(compose); + try { + if (!compose.serverId) { + return null; + } + const { stdout, stderr } = await execAsyncRemote( + compose.serverId, + `cat ${path}` + ); - if (stderr) { - return null; - } - if (!stdout) return null; - const parsedConfig = load(stdout) as ComposeSpecification; - return parsedConfig; - } catch (err) { - return null; - } + if (stderr) { + return null; + } + if (!stdout) return null; + const parsedConfig = load(stdout) as ComposeSpecification; + return parsedConfig; + } catch (err) { + return null; + } }; export const readComposeFile = async (compose: Compose) => { - const path = getComposePath(compose); - if (existsSync(path)) { - const yamlStr = readFileSync(path, "utf8"); - return yamlStr; - } - return null; + const path = getComposePath(compose); + if (existsSync(path)) { + const yamlStr = readFileSync(path, "utf8"); + return yamlStr; + } + return null; }; export const writeDomainsToCompose = async ( - compose: Compose, - domains: Domain[], + compose: Compose, + domains: Domain[] ) => { - if (!domains.length) { - return; - } - const composeConverted = await addDomainToCompose(compose, domains); + if (!domains.length) { + return; + } + const composeConverted = await addDomainToCompose(compose, domains); - const path = getComposePath(compose); - const composeString = dump(composeConverted, { lineWidth: 1000 }); - try { - await writeFile(path, composeString, "utf8"); - } catch (error) { - throw error; - } + const path = getComposePath(compose); + const composeString = dump(composeConverted, { lineWidth: 1000 }); + try { + await writeFile(path, composeString, "utf8"); + } catch (error) { + throw error; + } }; export const writeDomainsToComposeRemote = async ( - compose: Compose, - domains: Domain[], - logPath: string, + compose: Compose, + domains: Domain[], + logPath: string ) => { - if (!domains.length) { - return ""; - } + if (!domains.length) { + return ""; + } - try { - const composeConverted = await addDomainToCompose(compose, domains); - const path = getComposePath(compose); + try { + const composeConverted = await addDomainToCompose(compose, domains); + const path = getComposePath(compose); - if (!composeConverted) { - return ` + if (!composeConverted) { + return ` echo "❌ Error: Compose file not found" >> ${logPath}; exit 1; `; - } - if (compose.serverId) { - const composeString = dump(composeConverted, { lineWidth: 1000 }); - const encodedContent = encodeBase64(composeString); - return `echo "${encodedContent}" | base64 -d > "${path}";`; - } - } catch (error) { - // @ts-ignore - return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath}; + } + if (compose.serverId) { + const composeString = dump(composeConverted, { lineWidth: 1000 }); + const encodedContent = encodeBase64(composeString); + return `echo "${encodedContent}" | base64 -d > "${path}";`; + } + } catch (error) { + // @ts-ignore + return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath}; exit 1; `; - } + } }; // (node:59875) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit export const addDomainToCompose = async ( - compose: Compose, - domains: Domain[], + compose: Compose, + domains: Domain[] ) => { - const { appName } = compose; + const { appName } = compose; - let result: ComposeSpecification | null; + let result: ComposeSpecification | null; - if (compose.serverId) { - result = await loadDockerComposeRemote(compose); // aca hay que ir al servidor e ir a traer el compose file al servidor - } else { - result = await loadDockerCompose(compose); - } + if (compose.serverId) { + result = await loadDockerComposeRemote(compose); // aca hay que ir al servidor e ir a traer el compose file al servidor + } else { + result = await loadDockerCompose(compose); + } - if (!result || domains.length === 0) { - return null; - } + if (!result || domains.length === 0) { + return null; + } - if (compose.deployable) { - const randomized = randomizeDeployableSpecificationFile( - result, - compose.suffix || compose.appName, - ); - result = randomized; - } else if (compose.randomize) { - const randomized = randomizeSpecificationFile(result, compose.suffix); - result = randomized; - } + if (compose.isolatedDeployment) { + const randomized = randomizeDeployableSpecificationFile( + result, + compose.suffix || compose.appName + ); + result = randomized; + } else if (compose.randomize) { + const randomized = randomizeSpecificationFile(result, compose.suffix); + result = randomized; + } - for (const domain of domains) { - const { serviceName, https } = domain; - if (!serviceName) { - throw new Error("Service name not found"); - } - if (!result?.services?.[serviceName]) { - throw new Error(`The service ${serviceName} not found in the compose`); - } + for (const domain of domains) { + const { serviceName, https } = domain; + if (!serviceName) { + throw new Error("Service name not found"); + } + if (!result?.services?.[serviceName]) { + throw new Error(`The service ${serviceName} not found in the compose`); + } - const httpLabels = await createDomainLabels(appName, domain, "web"); - if (https) { - const httpsLabels = await createDomainLabels( - appName, - domain, - "websecure", - ); - httpLabels.push(...httpsLabels); - } + const httpLabels = await createDomainLabels(appName, domain, "web"); + if (https) { + const httpsLabels = await createDomainLabels( + appName, + domain, + "websecure" + ); + httpLabels.push(...httpsLabels); + } - let labels: DefinitionsService["labels"] = []; - if (compose.composeType === "docker-compose") { - if (!result.services[serviceName].labels) { - result.services[serviceName].labels = []; - } + let labels: DefinitionsService["labels"] = []; + if (compose.composeType === "docker-compose") { + if (!result.services[serviceName].labels) { + result.services[serviceName].labels = []; + } - labels = result.services[serviceName].labels; - } else { - // Stack Case - if (!result.services[serviceName].deploy) { - result.services[serviceName].deploy = {}; - } - if (!result.services[serviceName].deploy.labels) { - result.services[serviceName].deploy.labels = []; - } + labels = result.services[serviceName].labels; + } else { + // Stack Case + if (!result.services[serviceName].deploy) { + result.services[serviceName].deploy = {}; + } + if (!result.services[serviceName].deploy.labels) { + result.services[serviceName].deploy.labels = []; + } - labels = result.services[serviceName].deploy.labels; - } + labels = result.services[serviceName].deploy.labels; + } - if (Array.isArray(labels)) { - if (!labels.includes("traefik.enable=true")) { - labels.push("traefik.enable=true"); - } - labels.push(...httpLabels); - } + if (Array.isArray(labels)) { + if (!labels.includes("traefik.enable=true")) { + labels.push("traefik.enable=true"); + } + labels.push(...httpLabels); + } - if (!compose.deployable) { - // Add the dokploy-network to the service - result.services[serviceName].networks = addDokployNetworkToService( - result.services[serviceName].networks, - ); - } - } + if (!compose.isolatedDeployment) { + // Add the dokploy-network to the service + result.services[serviceName].networks = addDokployNetworkToService( + result.services[serviceName].networks + ); + } + } - // Add dokploy-network to the root of the compose file - if (!compose.deployable) { - result.networks = addDokployNetworkToRoot(result.networks); - } + // Add dokploy-network to the root of the compose file + if (!compose.isolatedDeployment) { + result.networks = addDokployNetworkToRoot(result.networks); + } - return result; + return result; }; export const writeComposeFile = async ( - compose: Compose, - composeSpec: ComposeSpecification, + compose: Compose, + composeSpec: ComposeSpecification ) => { - const path = getComposePath(compose); + const path = getComposePath(compose); - try { - const composeFile = dump(composeSpec, { - lineWidth: 1000, - }); - fs.writeFileSync(path, composeFile, "utf8"); - } catch (e) { - console.error("Error saving the YAML config file:", e); - } + try { + const composeFile = dump(composeSpec, { + lineWidth: 1000, + }); + fs.writeFileSync(path, composeFile, "utf8"); + } catch (e) { + console.error("Error saving the YAML config file:", e); + } }; export const createDomainLabels = async ( - appName: string, - domain: Domain, - entrypoint: "web" | "websecure", + appName: string, + domain: Domain, + entrypoint: "web" | "websecure" ) => { - const { host, port, https, uniqueConfigKey, certificateType, path } = domain; - const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`; - const labels = [ - `traefik.http.routers.${routerName}.rule=Host(\`${host}\`)${path && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`, - `traefik.http.routers.${routerName}.entrypoints=${entrypoint}`, - `traefik.http.services.${routerName}.loadbalancer.server.port=${port}`, - `traefik.http.routers.${routerName}.service=${routerName}`, - ]; + const { host, port, https, uniqueConfigKey, certificateType, path } = domain; + const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`; + const labels = [ + `traefik.http.routers.${routerName}.rule=Host(\`${host}\`)${path && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`, + `traefik.http.routers.${routerName}.entrypoints=${entrypoint}`, + `traefik.http.services.${routerName}.loadbalancer.server.port=${port}`, + `traefik.http.routers.${routerName}.service=${routerName}`, + ]; - if (entrypoint === "web" && https) { - labels.push( - `traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`, - ); - } + if (entrypoint === "web" && https) { + labels.push( + `traefik.http.routers.${routerName}.middlewares=redirect-to-https@file` + ); + } - if (entrypoint === "websecure") { - if (certificateType === "letsencrypt") { - labels.push( - `traefik.http.routers.${routerName}.tls.certresolver=letsencrypt`, - ); - } - } + if (entrypoint === "websecure") { + if (certificateType === "letsencrypt") { + labels.push( + `traefik.http.routers.${routerName}.tls.certresolver=letsencrypt` + ); + } + } - return labels; + return labels; }; export const addDokployNetworkToService = ( - networkService: DefinitionsService["networks"], + networkService: DefinitionsService["networks"] ) => { - let networks = networkService; - const network = "dokploy-network"; - if (!networks) { - networks = []; - } + let networks = networkService; + const network = "dokploy-network"; + if (!networks) { + networks = []; + } - if (Array.isArray(networks)) { - if (!networks.includes(network)) { - networks.push(network); - } - } else if (networks && typeof networks === "object") { - if (!(network in networks)) { - networks[network] = {}; - } - } + if (Array.isArray(networks)) { + if (!networks.includes(network)) { + networks.push(network); + } + } else if (networks && typeof networks === "object") { + if (!(network in networks)) { + networks[network] = {}; + } + } - return networks; + return networks; }; export const addDokployNetworkToRoot = ( - networkRoot: PropertiesNetworks | undefined, + networkRoot: PropertiesNetworks | undefined ) => { - let networks = networkRoot; - const network = "dokploy-network"; + let networks = networkRoot; + const network = "dokploy-network"; - if (!networks) { - networks = {}; - } + if (!networks) { + networks = {}; + } - if (networks[network] || !networks[network]) { - networks[network] = { - external: true, - }; - } + if (networks[network] || !networks[network]) { + networks[network] = { + external: true, + }; + } - return networks; + return networks; };