From 869e58739f43c76b2dabfc13dda1ed1a83bcc3d9 Mon Sep 17 00:00:00 2001 From: seppulcro Date: Tue, 17 Sep 2024 19:48:55 +0100 Subject: [PATCH 01/32] feat: add roundcube template --- apps/dokploy/public/templates/roundcube.svg | 15 ++++++++++++++ .../templates/roundcube/docker-compose.yml | 20 +++++++++++++++++++ apps/dokploy/templates/roundcube/index.ts | 11 ++++++++++ apps/dokploy/templates/templates.ts | 15 ++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 apps/dokploy/public/templates/roundcube.svg create mode 100644 apps/dokploy/templates/roundcube/docker-compose.yml create mode 100644 apps/dokploy/templates/roundcube/index.ts diff --git a/apps/dokploy/public/templates/roundcube.svg b/apps/dokploy/public/templates/roundcube.svg new file mode 100644 index 00000000..04238a06 --- /dev/null +++ b/apps/dokploy/public/templates/roundcube.svg @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/apps/dokploy/templates/roundcube/docker-compose.yml b/apps/dokploy/templates/roundcube/docker-compose.yml new file mode 100644 index 00000000..56defe04 --- /dev/null +++ b/apps/dokploy/templates/roundcube/docker-compose.yml @@ -0,0 +1,20 @@ +version: '2' +services: + roundcubemail: + image: roundcube/roundcubemail:latest + container_name: roundcubemail + volumes: + - ./www:/var/www/html + - ./db/sqlite:/var/roundcube/db + ports: + - '9002:80' + environment: + - ROUNDCUBEMAIL_DB_TYPE=sqlite + - ROUNDCUBEMAIL_SKIN=elastic + - ROUNDCUBEMAIL_DEFAULT_HOST=${DEFAULT_HOST} + - ROUNDCUBEMAIL_SMTP_SERVER=${SMTP_SERVER} + networks: + - dokploy-network +networks: + dokploy-network: + external: true \ No newline at end of file diff --git a/apps/dokploy/templates/roundcube/index.ts b/apps/dokploy/templates/roundcube/index.ts new file mode 100644 index 00000000..81ba7456 --- /dev/null +++ b/apps/dokploy/templates/roundcube/index.ts @@ -0,0 +1,11 @@ +import type { Schema, Template } from "../utils"; + +export function generate(schema: Schema): Template { + const envs = [ + "DEFAULT_HOST=tls://mail.example.com", + "SMTP_SERVER=tls://mail.example.com", + + ]; + + return { envs }; +} \ No newline at end of file diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 5603b188..4dcf85ca 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -497,4 +497,19 @@ export const templates: TemplateData[] = [ tags: ["self-hosted", "storage"], load: () => import("./gitea/index").then((m) => m.generate), }, + { + id: "roundcube", + name: "Roundcube", + version: "1.6.9", + description:"Free and open source webmail software for the masses, written in PHP.", + logo:"roundcube.svg", + links:{ + github:"", + website:"", + docs:"", + }, + tags:["self-hosted","mail","webmail"], + load: () => import("./roundcube/index").then((m) => m.generate), + + } ]; From 8e227a3286baf4d486b49219eefa50044daaf7ff Mon Sep 17 00:00:00 2001 From: seppulcro Date: Thu, 19 Sep 2024 08:37:37 +0100 Subject: [PATCH 02/32] fix: update roundcube template to match spec --- .../templates/roundcube/docker-compose.yml | 7 ++----- apps/dokploy/templates/roundcube/index.ts | 21 +++++++++++++++---- apps/dokploy/templates/templates.ts | 18 ++++++++-------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/apps/dokploy/templates/roundcube/docker-compose.yml b/apps/dokploy/templates/roundcube/docker-compose.yml index 56defe04..562f350f 100644 --- a/apps/dokploy/templates/roundcube/docker-compose.yml +++ b/apps/dokploy/templates/roundcube/docker-compose.yml @@ -1,13 +1,9 @@ -version: '2' services: roundcubemail: image: roundcube/roundcubemail:latest - container_name: roundcubemail volumes: - ./www:/var/www/html - ./db/sqlite:/var/roundcube/db - ports: - - '9002:80' environment: - ROUNDCUBEMAIL_DB_TYPE=sqlite - ROUNDCUBEMAIL_SKIN=elastic @@ -15,6 +11,7 @@ services: - ROUNDCUBEMAIL_SMTP_SERVER=${SMTP_SERVER} networks: - dokploy-network + networks: dokploy-network: - external: true \ No newline at end of file + external: true``` diff --git a/apps/dokploy/templates/roundcube/index.ts b/apps/dokploy/templates/roundcube/index.ts index 81ba7456..8df8c743 100644 --- a/apps/dokploy/templates/roundcube/index.ts +++ b/apps/dokploy/templates/roundcube/index.ts @@ -1,11 +1,24 @@ -import type { Schema, Template } from "../utils"; +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; export function generate(schema: Schema): Template { + const randomDomain = generateRandomDomain(schema); const envs = [ "DEFAULT_HOST=tls://mail.example.com", "SMTP_SERVER=tls://mail.example.com", - ]; - return { envs }; -} \ No newline at end of file + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 80, + serviceName: "roundcubemail", + }, + ]; + + return { envs, domains }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 4dcf85ca..ebd0a649 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -501,15 +501,15 @@ export const templates: TemplateData[] = [ id: "roundcube", name: "Roundcube", version: "1.6.9", - description:"Free and open source webmail software for the masses, written in PHP.", - logo:"roundcube.svg", - links:{ - github:"", - website:"", - docs:"", + description: + "Free and open source webmail software for the masses, written in PHP.", + logo: "roundcube.svg", + links: { + github: "https://github.com/roundcube/roundcubemail", + website: "https://roundcube.net/", + docs: "https://roundcube.net/about/", }, - tags:["self-hosted","mail","webmail"], + tags: ["self-hosted", "mail", "webmail"], load: () => import("./roundcube/index").then((m) => m.generate), - - } + }, ]; From 5b0bf99cbfb05773f39957b418d50c5e49752c1d Mon Sep 17 00:00:00 2001 From: seppulcro Date: Thu, 19 Sep 2024 14:40:20 +0100 Subject: [PATCH 03/32] fix: update roundcube template remove bad chars --- apps/dokploy/templates/roundcube/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/roundcube/docker-compose.yml b/apps/dokploy/templates/roundcube/docker-compose.yml index 562f350f..4dd6097a 100644 --- a/apps/dokploy/templates/roundcube/docker-compose.yml +++ b/apps/dokploy/templates/roundcube/docker-compose.yml @@ -14,4 +14,4 @@ services: networks: dokploy-network: - external: true``` + external: true From 59386ed4b7314c405348fedf94d687a1c25cd8fb Mon Sep 17 00:00:00 2001 From: seppulcro Date: Mon, 23 Sep 2024 16:13:16 +0100 Subject: [PATCH 04/32] fix: Update docs; Fix useMDXComponents for li override: add missing id for correct remark-gfm footnotes functionallity --- .../content/docs/core/templates/overview.mdx | 5 ++-- apps/docs/mdx-components.tsx | 24 ++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/docs/content/docs/core/templates/overview.mdx b/apps/docs/content/docs/core/templates/overview.mdx index 363650f6..6c8a7e8c 100644 --- a/apps/docs/content/docs/core/templates/overview.mdx +++ b/apps/docs/content/docs/core/templates/overview.mdx @@ -31,8 +31,7 @@ The following templates are available: - **Wordpress**: Open Source Content Management System - **Open WebUI**: Free and Open Source ChatGPT Alternative - **Teable**: Open Source Airtable Alternative, Developer Friendly, No-code Database Built on Postgres - - +- **Roundcube**: Free and open source webmail software for the masses, written in PHP, uses SMTP[^1]. ## Create your own template @@ -41,3 +40,5 @@ We accept contributions to upload new templates to the dokploy repository. Make sure to follow the guidelines for creating a template: [Steps to create your own template](https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#templates) + +[^1]: Please note that if you're self-hosting a mail server you need port 25 to be open for SMTP (Mail Transmission Protocol that allows you to send and receive) to work properly. Some VPS providers like [Hetzner](https://docs.hetzner.com/cloud/servers/faq/#why-can-i-not-send-any-mails-from-my-server) block this port by default for new clients. diff --git a/apps/docs/mdx-components.tsx b/apps/docs/mdx-components.tsx index 96c5b74b..913f33e1 100644 --- a/apps/docs/mdx-components.tsx +++ b/apps/docs/mdx-components.tsx @@ -3,15 +3,17 @@ import defaultComponents from "fumadocs-ui/mdx"; import type { MDXComponents } from "mdx/types"; export function useMDXComponents(components: MDXComponents): MDXComponents { - return { - ...defaultComponents, - ...components, - ImageZoom, - p: ({ children }) => ( -

{children}

- ), - li: ({ children }) => ( -
  • {children}
  • - ), - }; + return { + ...defaultComponents, + ...components, + ImageZoom, + p: ({ children }) => ( +

    {children}

    + ), + li: ({ children, id }) => ( +
  • + {children} +
  • + ), + }; } From 4715f34e1516400df3e245d65359f0f2f2eb2714 Mon Sep 17 00:00:00 2001 From: seppulcro Date: Tue, 24 Sep 2024 17:01:24 +0100 Subject: [PATCH 05/32] fix: mdx-components formatting with biome --- apps/docs/mdx-components.tsx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/docs/mdx-components.tsx b/apps/docs/mdx-components.tsx index 913f33e1..10488dca 100644 --- a/apps/docs/mdx-components.tsx +++ b/apps/docs/mdx-components.tsx @@ -3,17 +3,17 @@ import defaultComponents from "fumadocs-ui/mdx"; import type { MDXComponents } from "mdx/types"; export function useMDXComponents(components: MDXComponents): MDXComponents { - return { - ...defaultComponents, - ...components, - ImageZoom, - p: ({ children }) => ( -

    {children}

    - ), - li: ({ children, id }) => ( -
  • - {children} -
  • - ), - }; + return { + ...defaultComponents, + ...components, + ImageZoom, + p: ({ children }) => ( +

    {children}

    + ), + li: ({ children, id }) => ( +
  • + {children} +
  • + ), + }; } From 0aff344bc0fa513e9f8aa24f63794090f3d0757c Mon Sep 17 00:00:00 2001 From: seppulcro Date: Tue, 24 Sep 2024 17:27:11 +0100 Subject: [PATCH 06/32] fix: change tags for roundcube template --- apps/dokploy/templates/templates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index ebd0a649..afe9d1b6 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -509,7 +509,7 @@ export const templates: TemplateData[] = [ website: "https://roundcube.net/", docs: "https://roundcube.net/about/", }, - tags: ["self-hosted", "mail", "webmail"], + tags: ["self-hosted", "email", "webmail"], load: () => import("./roundcube/index").then((m) => m.generate), }, ]; From 3b9f5d6f5c73f1d13c9e5ecd37b2bb11eef3db57 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 26 Sep 2024 13:14:14 +0200 Subject: [PATCH 07/32] feat: add presets for add-redirect dialog --- .../advanced/redirects/add-redirect.tsx | 85 +++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx b/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx index efe1b9ac..320471ba 100644 --- a/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx @@ -19,6 +19,15 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -36,6 +45,36 @@ const AddRedirectchema = z.object({ type AddRedirect = z.infer; +// Default presets +const redirectPresets = [ + // { + // label: "Allow www & non-www.", + // redirect: { + // regex: "", + // permanent: false, + // replacement: "", + // }, + // }, + { + id: "to-www", + label: "Redirect to www", + redirect: { + regex: "^https?://(?:www.)?(.+)", + permanent: true, + replacement: "https://www.$${1}", + }, + }, + { + id: "to-non-www", + label: "Redirect to non-www", + redirect: { + regex: "^https?://www.(.+)", + permanent: true, + replacement: "https://$${1}", + }, + }, +]; + interface Props { applicationId: string; children?: React.ReactNode; @@ -43,9 +82,10 @@ interface Props { export const AddRedirect = ({ applicationId, - children = , + children = , }: Props) => { const [isOpen, setIsOpen] = useState(false); + const [presetSelected, setPresetSelected] = useState(""); const utils = api.useUtils(); const { mutateAsync, isLoading, error, isError } = @@ -81,19 +121,36 @@ export const AddRedirect = ({ await utils.application.readTraefikConfig.invalidate({ applicationId, }); - setIsOpen(false); + onDialogToggle(false); }) .catch(() => { toast.error("Error to create the redirect"); }); }; + const onDialogToggle = (open: boolean) => { + setIsOpen(open); + // commented for the moment because not reseting the form if accidentally closed the dialog can be considered as a feature instead of a bug + // setPresetSelected(""); + // form.reset(); + }; + + const onPresetSelect = (presetId: string) => { + const redirectPreset = redirectPresets.find( + (preset) => preset.id === presetId, + )?.redirect; + if (!redirectPreset) return; + const { regex, permanent, replacement } = redirectPreset; + form.reset({ regex, permanent, replacement }, { keepDefaultValues: true }); + setPresetSelected(presetId); + }; + return ( - + - + Redirects @@ -102,6 +159,24 @@ export const AddRedirect = ({ {isError && {error?.message}} +
    + + +
    + + +
    ( - +
    Permanent From 9059f42b03d3567759aba18136b7214dde91ef7b Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 26 Sep 2024 13:53:10 +0200 Subject: [PATCH 08/32] refactor: display certificate select field after https switch field inside add-domain dialogs --- .../application/domains/add-domain.tsx | 47 +++++++++--------- .../dashboard/compose/domains/add-domain.tsx | 49 ++++++++++--------- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index 43a3cb69..7ab67a29 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -140,7 +140,7 @@ export const AddDomain = ({ {children} - + Domain {dictionary.dialogDescription} @@ -241,6 +241,29 @@ export const AddDomain = ({ ); }} /> + + ( + +
    + HTTPS + + Automatically provision SSL Certificate. + + +
    + + + +
    + )} + /> + {form.getValues().https && ( )} - - ( - -
    - HTTPS - - Automatically provision SSL Certificate. - - -
    - - - -
    - )} - />
    diff --git a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx index 056c003a..91f211d1 100644 --- a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx @@ -161,7 +161,7 @@ export const AddDomainCompose = ({ {children} - + Domain {dictionary.dialogDescription} @@ -190,7 +190,7 @@ export const AddDomainCompose = ({ {errorServices?.message} )} -
    +
    + + ( + +
    + HTTPS + + Automatically provision SSL Certificate. + + +
    + + + +
    + )} + /> + {https && ( )} - - ( - -
    - HTTPS - - Automatically provision SSL Certificate. - - -
    - - - -
    - )} - />
    From 65c1001751642c73ddc560805006b8894749004b Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:22:52 -0600 Subject: [PATCH 09/32] fix(swagger): add mising validation --- apps/dokploy/server/auth/token.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/dokploy/server/auth/token.ts b/apps/dokploy/server/auth/token.ts index 54162fdc..64009fef 100644 --- a/apps/dokploy/server/auth/token.ts +++ b/apps/dokploy/server/auth/token.ts @@ -2,6 +2,8 @@ import type { IncomingMessage } from "node:http"; import { TimeSpan } from "lucia"; import { Lucia } from "lucia/dist/core.js"; import { type ReturnValidateToken, adapter } from "./auth"; +import { findAdminByAuthId } from "../api/services/admin"; +import { findUserByAuthId } from "../api/services/user"; export const luciaToken = new Lucia(adapter, { sessionCookie: { @@ -31,6 +33,16 @@ export const validateBearerToken = async ( }; } const result = await luciaToken.validateSession(sessionId); + + if (result.user) { + if (result.user?.rol === "admin") { + const admin = await findAdminByAuthId(result.user.id); + result.user.adminId = admin.adminId; + } else if (result.user?.rol === "user") { + const userResult = await findUserByAuthId(result.user.id); + result.user.adminId = userResult.adminId; + } + } return { session: result.session, ...((result.user && { From bbfe095045cba813654bb583c11f982f887c9954 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:23:44 -0600 Subject: [PATCH 10/32] chore(version): bump version --- apps/dokploy/package.json | 2 +- apps/dokploy/server/auth/token.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 02321899..f5b57635 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.9.0", + "version": "v0.9.1", "private": true, "license": "Apache-2.0", "type": "module", diff --git a/apps/dokploy/server/auth/token.ts b/apps/dokploy/server/auth/token.ts index 64009fef..5581777a 100644 --- a/apps/dokploy/server/auth/token.ts +++ b/apps/dokploy/server/auth/token.ts @@ -1,9 +1,9 @@ import type { IncomingMessage } from "node:http"; import { TimeSpan } from "lucia"; import { Lucia } from "lucia/dist/core.js"; -import { type ReturnValidateToken, adapter } from "./auth"; import { findAdminByAuthId } from "../api/services/admin"; import { findUserByAuthId } from "../api/services/user"; +import { type ReturnValidateToken, adapter } from "./auth"; export const luciaToken = new Lucia(adapter, { sessionCookie: { From 3dc3672406f68b457e9e2b5b64f4a20699935bb8 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 27 Sep 2024 19:30:41 +0200 Subject: [PATCH 11/32] docs: add explanation how to use multiline env variables --- apps/docs/content/docs/core/application/overview.mdx | 2 ++ apps/docs/content/docs/core/databases/overview.mdx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/docs/content/docs/core/application/overview.mdx b/apps/docs/content/docs/core/application/overview.mdx index 788daff8..9f881f6b 100644 --- a/apps/docs/content/docs/core/application/overview.mdx +++ b/apps/docs/content/docs/core/application/overview.mdx @@ -15,6 +15,8 @@ Configure the source of your code, the way your application is built, and also m If you need to assign environment variables to your application, you can do so here. +In case you need to use a multiline variable, you can wrap it in double quotes just like this `'"here_is_my_private_key"'`. + ## Monitoring Four graphs will be displayed for the use of memory, CPU, disk, and network. Note that the information is only updated if you are viewing the current page, otherwise it will not be updated. diff --git a/apps/docs/content/docs/core/databases/overview.mdx b/apps/docs/content/docs/core/databases/overview.mdx index f9702fb0..0fd2f5b0 100644 --- a/apps/docs/content/docs/core/databases/overview.mdx +++ b/apps/docs/content/docs/core/databases/overview.mdx @@ -26,6 +26,8 @@ Actions like deploying, updating, and deleting your database, and stopping it. If you need to assign environment variables to your application, you can do so here. +In case you need to use a multiline variable, you can wrap it in double quotes just like this `'"here_is_my_private_key"'`. + ## Monitoring Four graphs will be displayed for the use of memory, CPU, disk, and network. Note that the information is only updated if you are viewing the current page, otherwise it will not be updated. From b48b9765cd4aabf281734f032560eea4c05bfa3b Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:23:30 -0600 Subject: [PATCH 12/32] fix(nixpacks): adjust build path on nixpacks static --- .../dokploy/server/utils/builders/nixpacks.ts | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/dokploy/server/utils/builders/nixpacks.ts b/apps/dokploy/server/utils/builders/nixpacks.ts index 0f408743..4ae4b2cb 100644 --- a/apps/dokploy/server/utils/builders/nixpacks.ts +++ b/apps/dokploy/server/utils/builders/nixpacks.ts @@ -1,4 +1,4 @@ -import type { WriteStream } from "node:fs"; +import { mkdirSync, type WriteStream, existsSync } from "node:fs"; import path from "node:path"; import { buildStatic, getStaticCommand } from "@/server/utils/builders/static"; import { nanoid } from "nanoid"; @@ -42,7 +42,6 @@ export const buildNixpacks = async ( and copy the artifacts on the host filesystem. Then, remove the container and create a static build. */ - if (publishDirectory) { await spawnAsync( "docker", @@ -50,12 +49,22 @@ export const buildNixpacks = async ( writeToStream, ); + const localPath = path.join(buildAppDirectory, publishDirectory); + + if (!existsSync(path.dirname(localPath))) { + mkdirSync(path.dirname(localPath), { recursive: true }); + } + + // https://docs.docker.com/reference/cli/docker/container/cp/ + const isDirectory = + publishDirectory.endsWith("/") || !path.extname(publishDirectory); + await spawnAsync( "docker", [ "cp", - `${buildContainerId}:/app/${publishDirectory}`, - path.join(buildAppDirectory, publishDirectory), + `${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""}`, + localPath, ], writeToStream, ); @@ -108,9 +117,14 @@ echo "✅ Nixpacks build completed." >> ${logPath}; Then, remove the container and create a static build. */ if (publishDirectory) { + const localPath = path.join(buildAppDirectory, publishDirectory); + const isDirectory = + publishDirectory.endsWith("/") || !path.extname(publishDirectory); + bashCommand += ` docker create --name ${buildContainerId} ${appName} -docker cp ${buildContainerId}:/app/${publishDirectory} ${path.join(buildAppDirectory, publishDirectory)} >> ${logPath} 2>> ${logPath} || { +mkdir -p ${localPath} +docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} >> ${logPath} 2>> ${logPath} || { docker rm ${buildContainerId} echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" >> ${logPath}; exit 1; From bf65bc9462785e5125755529e1839fe48e0c01ef Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:25:06 -0600 Subject: [PATCH 13/32] chore(lint): format --- apps/dokploy/server/utils/builders/nixpacks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/server/utils/builders/nixpacks.ts b/apps/dokploy/server/utils/builders/nixpacks.ts index 4ae4b2cb..2d81a7c0 100644 --- a/apps/dokploy/server/utils/builders/nixpacks.ts +++ b/apps/dokploy/server/utils/builders/nixpacks.ts @@ -1,4 +1,4 @@ -import { mkdirSync, type WriteStream, existsSync } from "node:fs"; +import { type WriteStream, existsSync, mkdirSync } from "node:fs"; import path from "node:path"; import { buildStatic, getStaticCommand } from "@/server/utils/builders/static"; import { nanoid } from "nanoid"; From 7027f39c486aca8424ea3a7d0e74835347a5c5c5 Mon Sep 17 00:00:00 2001 From: Vincent Young Date: Sat, 28 Sep 2024 15:06:20 -0400 Subject: [PATCH 14/32] feat: add update option --- apps/website/public/install.sh | 212 ++++++++++++++++++--------------- 1 file changed, 115 insertions(+), 97 deletions(-) diff --git a/apps/website/public/install.sh b/apps/website/public/install.sh index e68a59fd..f5b318f2 100644 --- a/apps/website/public/install.sh +++ b/apps/website/public/install.sh @@ -1,112 +1,130 @@ #!/bin/bash +install_dokploy() { + if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" >&2 + exit 1 + fi -if [ "$(id -u)" != "0" ]; then - echo "This script must be run as root" >&2 - exit 1 -fi + # check if is Mac OS + if [ "$(uname)" = "Darwin" ]; then + echo "This script must be run on Linux" >&2 + exit 1 + fi -# check if is Mac OS -if [ "$(uname)" = "Darwin" ]; then - echo "This script must be run on Linux" >&2 - exit 1 -fi + # check if is running inside a container + if [ -f /.dockerenv ]; then + echo "This script must be run on Linux" >&2 + exit 1 + fi + # check if something is running on port 80 + if ss -tulnp | grep ':80 ' >/dev/null; then + echo "Error: something is already running on port 80" >&2 + exit 1 + fi -# check if is running inside a container -if [ -f /.dockerenv ]; then - echo "This script must be run on Linux" >&2 - exit 1 -fi + # check if something is running on port 443 + if ss -tulnp | grep ':443 ' >/dev/null; then + echo "Error: something is already running on port 443" >&2 + exit 1 + fi -# check if something is running on port 80 -if ss -tulnp | grep ':80 ' >/dev/null; then - echo "Error: something is already running on port 80" >&2 - exit 1 -fi + command_exists() { + command -v "$@" > /dev/null 2>&1 + } -# check if something is running on port 443 -if ss -tulnp | grep ':443 ' >/dev/null; then - echo "Error: something is already running on port 443" >&2 - exit 1 -fi - -command_exists() { - command -v "$@" > /dev/null 2>&1 -} - -if command_exists docker; then - echo "Docker already installed" -else - curl -sSL https://get.docker.com | sh -fi - -docker swarm leave --force 2>/dev/null - -get_ip() { - # Try to get IPv4 - local ipv4=$(curl -4s https://ifconfig.io 2>/dev/null) - - if [ -n "$ipv4" ]; then - echo "$ipv4" + if command_exists docker; then + echo "Docker already installed" else - # Try to get IPv6 - local ipv6=$(curl -6s https://ifconfig.io 2>/dev/null) - if [ -n "$ipv6" ]; then - echo "$ipv6" + curl -sSL https://get.docker.com | sh + fi + + docker swarm leave --force 2>/dev/null + + get_ip() { + # Try to get IPv4 + local ipv4=$(curl -4s https://ifconfig.io 2>/dev/null) + + if [ -n "$ipv4" ]; then + echo "$ipv4" + else + # Try to get IPv6 + local ipv6=$(curl -6s https://ifconfig.io 2>/dev/null) + if [ -n "$ipv6" ]; then + echo "$ipv6" + fi fi - fi + } + + advertise_addr=$(get_ip) + + docker swarm init --advertise-addr $advertise_addr + + echo "Swarm initialized" + + docker network rm -f dokploy-network 2>/dev/null + docker network create --driver overlay --attachable dokploy-network + + echo "Network created" + + mkdir -p /etc/dokploy + + chmod 777 /etc/dokploy + + docker pull dokploy/dokploy:latest + + # Installation + docker service create \ + --name dokploy \ + --replicas 1 \ + --network dokploy-network \ + --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ + --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ + --mount type=volume,source=dokploy-docker-config,target=/root/.docker \ + --publish published=3000,target=3000,mode=host \ + --update-parallelism 1 \ + --update-order stop-first \ + --constraint 'node.role == manager' \ + dokploy/dokploy:latest + + GREEN="\033[0;32m" + YELLOW="\033[1;33m" + BLUE="\033[0;34m" + NC="\033[0m" # No Color + + format_ip_for_url() { + local ip="$1" + if echo "$ip" | grep -q ':'; then + # IPv6 + echo "[${ip}]" + else + # IPv4 + echo "${ip}" + fi + } + + formatted_addr=$(format_ip_for_url "$advertise_addr") + echo "" + printf "${GREEN}Congratulations, Dokploy is installed!${NC}\n" + printf "${BLUE}Wait 15 seconds for the server to start${NC}\n" + printf "${YELLOW}Please go to http://${formatted_addr}:3000${NC}\n\n" } -advertise_addr=$(get_ip) +update_dokploy() { + echo "Updating Dokploy..." + + # Pull the latest image + docker pull dokploy/dokploy:latest -docker swarm init --advertise-addr $advertise_addr + # Update the service + docker service update --image dokploy/dokploy:latest dokploy -echo "Swarm initialized" - -docker network rm -f dokploy-network 2>/dev/null -docker network create --driver overlay --attachable dokploy-network - -echo "Network created" - -mkdir -p /etc/dokploy - -chmod 777 /etc/dokploy - -docker pull dokploy/dokploy:latest - -# Installation -docker service create \ - --name dokploy \ - --replicas 1 \ - --network dokploy-network \ - --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ - --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ - --mount type=volume,source=dokploy-docker-config,target=/root/.docker \ - --publish published=3000,target=3000,mode=host \ - --update-parallelism 1 \ - --update-order stop-first \ - --constraint 'node.role == manager' \ - dokploy/dokploy:latest - -GREEN="\033[0;32m" -YELLOW="\033[1;33m" -BLUE="\033[0;34m" -NC="\033[0m" # No Color - -format_ip_for_url() { - local ip="$1" - if echo "$ip" | grep -q ':'; then - # IPv6 - echo "[${ip}]" - else - # IPv4 - echo "${ip}" - fi + echo "Dokploy has been updated to the latest version." } -formatted_addr=$(format_ip_for_url "$advertise_addr") -echo "" -printf "${GREEN}Congratulations, Dokploy is installed!${NC}\n" -printf "${BLUE}Wait 15 seconds for the server to start${NC}\n" -printf "${YELLOW}Please go to http://${formatted_addr}:3000${NC}\n\n" -echo "" +# Main script execution +if [ "$1" = "update" ]; then + update_dokploy +else + install_dokploy +fi From cef21ac8b5bae06e1054a1f11fd032d8612a0664 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:31:38 -0600 Subject: [PATCH 15/32] fix(multi-server): remove string validation on port --- .../dashboard/settings/servers/add-server.tsx | 16 +++++++++++++++- .../dashboard/settings/servers/update-server.tsx | 16 +++++++++++++++- apps/dokploy/package.json | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/add-server.tsx b/apps/dokploy/components/dashboard/settings/servers/add-server.tsx index 6bd44dcf..8cb71167 100644 --- a/apps/dokploy/components/dashboard/settings/servers/add-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/add-server.tsx @@ -212,7 +212,21 @@ export const AddServer = () => { Port - + { + const value = e.target.value; + if (value === "") { + field.onChange(0); + } else { + const number = Number.parseInt(value, 10); + if (!Number.isNaN(number)) { + field.onChange(number); + } + } + }} + /> diff --git a/apps/dokploy/components/dashboard/settings/servers/update-server.tsx b/apps/dokploy/components/dashboard/settings/servers/update-server.tsx index 73eadd30..16eb5ba7 100644 --- a/apps/dokploy/components/dashboard/settings/servers/update-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/update-server.tsx @@ -228,7 +228,21 @@ export const UpdateServer = ({ serverId }: Props) => { Port - + { + const value = e.target.value; + if (value === "") { + field.onChange(0); + } else { + const number = Number.parseInt(value, 10); + if (!Number.isNaN(number)) { + field.onChange(number); + } + } + }} + /> diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index f5b57635..7d5c28d3 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.9.1", + "version": "v0.9.2", "private": true, "license": "Apache-2.0", "type": "module", From 6cc64b4454a632bfe05896584ffd822c41915a49 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:24:09 -0600 Subject: [PATCH 16/32] refactior(terminal): add port to server connect --- apps/dokploy/server/wss/terminal.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index 4d8f03f6..2f2ff1a8 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -72,6 +72,8 @@ export const setupTerminalWebSocketServer = ( "StrictHostKeyChecking=no", "-i", privateKey, + "-p", + `${server.port}`, `${server.username}@${server.ipAddress}`, ]; const ptyProcess = spawn("ssh", sshCommand.slice(1), { From 0b2b20caeb70c63a6a3bcae72317b68234e16694 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:24:35 -0600 Subject: [PATCH 17/32] chore(version): bump version --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 7d5c28d3..bb79865e 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.9.2", + "version": "v0.9.3", "private": true, "license": "Apache-2.0", "type": "module", From 18b4b23f79a9a76d1832b3ac7ef27719e0e17f5c Mon Sep 17 00:00:00 2001 From: Vincent Yang Date: Sun, 29 Sep 2024 22:45:39 -0400 Subject: [PATCH 18/32] feat: add update option for canary and feature tag --- apps/website/public/canary.sh | 215 ++++++++++++++++++--------------- apps/website/public/feature.sh | 206 +++++++++++++++++-------------- 2 files changed, 230 insertions(+), 191 deletions(-) diff --git a/apps/website/public/canary.sh b/apps/website/public/canary.sh index 3a9102b0..32f95bff 100644 --- a/apps/website/public/canary.sh +++ b/apps/website/public/canary.sh @@ -1,114 +1,133 @@ #!/bin/bash +install_dokploy() { + if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" >&2 + exit 1 + fi -if [ "$(id -u)" != "0" ]; then - echo "This script must be run as root" >&2 - exit 1 -fi - -# check if is Mac OS -if [ "$(uname)" = "Darwin" ]; then - echo "This script must be run on Linux" >&2 - exit 1 -fi + # check if is Mac OS + if [ "$(uname)" = "Darwin" ]; then + echo "This script must be run on Linux" >&2 + exit 1 + fi -# check if is running inside a container -if [ -f /.dockerenv ]; then - echo "This script must be run on Linux" >&2 - exit 1 -fi + # check if is running inside a container + if [ -f /.dockerenv ]; then + echo "This script must be run on Linux" >&2 + exit 1 + fi -# check if something is running on port 80 -if ss -tulnp | grep ':80 ' >/dev/null; then - echo "Error: something is already running on port 80" >&2 - exit 1 -fi + # check if something is running on port 80 + if ss -tulnp | grep ':80 ' >/dev/null; then + echo "Error: something is already running on port 80" >&2 + exit 1 + fi -# check if something is running on port 443 -if ss -tulnp | grep ':443 ' >/dev/null; then - echo "Error: something is already running on port 443" >&2 - exit 1 -fi + # check if something is running on port 443 + if ss -tulnp | grep ':443 ' >/dev/null; then + echo "Error: something is already running on port 443" >&2 + exit 1 + fi -command_exists() { - command -v "$@" > /dev/null 2>&1 -} + command_exists() { + command -v "$@" > /dev/null 2>&1 + } -if command_exists docker; then - echo "Docker already installed" -else - curl -sSL https://get.docker.com | sh -fi - -docker swarm leave --force 2>/dev/null - -get_ip() { - # Try to get IPv4 - local ipv4=$(curl -4s https://ifconfig.io 2>/dev/null) - - if [ -n "$ipv4" ]; then - echo "$ipv4" + if command_exists docker; then + echo "Docker already installed" else - # Try to get IPv6 - local ipv6=$(curl -6s https://ifconfig.io 2>/dev/null) - if [ -n "$ipv6" ]; then - echo "$ipv6" + curl -sSL https://get.docker.com | sh + fi + + docker swarm leave --force 2>/dev/null + + get_ip() { + # Try to get IPv4 + local ipv4=$(curl -4s https://ifconfig.io 2>/dev/null) + + if [ -n "$ipv4" ]; then + echo "$ipv4" + else + # Try to get IPv6 + local ipv6=$(curl -6s https://ifconfig.io 2>/dev/null) + if [ -n "$ipv6" ]; then + echo "$ipv6" + fi fi - fi + } + + advertise_addr=$(get_ip) + + docker swarm init --advertise-addr $advertise_addr + + echo "Swarm initialized" + + docker network rm -f dokploy-network 2>/dev/null + docker network create --driver overlay --attachable dokploy-network + + echo "Network created" + + mkdir -p /etc/dokploy + + chmod 777 /etc/dokploy + + docker pull dokploy/dokploy:canary + + # Installation + docker service create \ + --name dokploy \ + --replicas 1 \ + --network dokploy-network \ + --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ + --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ + --mount type=volume,source=dokploy-docker-config,target=/root/.docker \ + --publish published=3000,target=3000,mode=host \ + --update-parallelism 1 \ + --update-order stop-first \ + --constraint 'node.role == manager' \ + -e RELEASE_TAG=canary \ + dokploy/dokploy:canary + + GREEN="\033[0;32m" + YELLOW="\033[1;33m" + BLUE="\033[0;34m" + NC="\033[0m" # No Color + + format_ip_for_url() { + local ip="$1" + if echo "$ip" | grep -q ':'; then + # IPv6 + echo "[${ip}]" + else + # IPv4 + echo "${ip}" + fi + } + + formatted_addr=$(format_ip_for_url "$advertise_addr") + echo "" + printf "${GREEN}Congratulations, Dokploy is installed!${NC}\n" + printf "${BLUE}Wait 15 seconds for the server to start${NC}\n" + printf "${YELLOW}Please go to http://${formatted_addr}:3000${NC}\n\n" + echo "" } -advertise_addr=$(get_ip) +update_dokploy() { + echo "Updating Dokploy..." + + # Pull the latest canary image + docker pull dokploy/dokploy:canary -docker swarm init --advertise-addr $advertise_addr + # Update the service + docker service update --image dokploy/dokploy:canary dokploy -echo "Swarm initialized" - -docker network rm -f dokploy-network 2>/dev/null -docker network create --driver overlay --attachable dokploy-network - -echo "Network created" - -mkdir -p /etc/dokploy - -chmod 777 /etc/dokploy - -docker pull dokploy/dokploy:canary - -# Installation -docker service create \ - --name dokploy \ - --replicas 1 \ - --network dokploy-network \ - --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ - --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ - --mount type=volume,source=dokploy-docker-config,target=/root/.docker \ - --publish published=3000,target=3000,mode=host \ - --update-parallelism 1 \ - --update-order stop-first \ - --constraint 'node.role == manager' \ - -e RELEASE_TAG=canary \ - dokploy/dokploy:canary - -GREEN="\033[0;32m" -YELLOW="\033[1;33m" -BLUE="\033[0;34m" -NC="\033[0m" # No Color - -format_ip_for_url() { - local ip="$1" - if echo "$ip" | grep -q ':'; then - # IPv6 - echo "[${ip}]" - else - # IPv4 - echo "${ip}" - fi + echo "Dokploy has been updated to the latest canary version." } -formatted_addr=$(format_ip_for_url "$advertise_addr") -echo "" -printf "${GREEN}Congratulations, Dokploy is installed!${NC}\n" -printf "${BLUE}Wait 15 seconds for the server to start${NC}\n" -printf "${YELLOW}Please go to http://${formatted_addr}:3000${NC}\n\n" -echo "" - +# Main script execution +if [ "$1" = "update" ]; then + update_dokploy +else + install_dokploy +fi \ No newline at end of file diff --git a/apps/website/public/feature.sh b/apps/website/public/feature.sh index 453da012..fca4fccd 100644 --- a/apps/website/public/feature.sh +++ b/apps/website/public/feature.sh @@ -1,97 +1,117 @@ #!/bin/bash - -if [ "$(id -u)" != "0" ]; then - echo "This script must be run as root" >&2 - exit 1 -fi - -# check if is Mac OS -if [ "$(uname)" = "Darwin" ]; then - echo "This script must be run on Linux" >&2 - exit 1 -fi - - -# check if is running inside a container -if [ -f /.dockerenv ]; then - echo "This script must be run on Linux" >&2 - exit 1 -fi - -# check if something is running on port 80 -if ss -tulnp | grep ':80 ' >/dev/null; then - echo "Error: something is already running on port 80" >&2 - exit 1 -fi - -# check if something is running on port 443 -if ss -tulnp | grep ':443 ' >/dev/null; then - echo "Error: something is already running on port 443" >&2 - exit 1 -fi - -command_exists() { - command -v "$@" > /dev/null 2>&1 -} - -if command_exists docker; then - echo "Docker already installed" -else - curl -sSL https://get.docker.com | sh -fi - -docker swarm leave --force 2>/dev/null -advertise_addr=$(curl -s ifconfig.me) - -docker swarm init --advertise-addr $advertise_addr - -echo "Swarm initialized" - -docker network rm -f dokploy-network 2>/dev/null -docker network create --driver overlay --attachable dokploy-network - -echo "Network created" - -mkdir -p /etc/dokploy - -chmod 777 /etc/dokploy - -docker pull dokploy/dokploy:feature - -# Installation -docker service create \ - --name dokploy \ - --replicas 1 \ - --network dokploy-network \ - --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ - --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ - --mount type=volume,source=dokploy-docker-config,target=/root/.docker \ - --publish published=3000,target=3000,mode=host \ - --update-parallelism 1 \ - --update-order stop-first \ - --constraint 'node.role == manager' \ - -e RELEASE_TAG=feature \ - dokploy/dokploy:feature - -GREEN="\033[0;32m" -YELLOW="\033[1;33m" -BLUE="\033[0;34m" -NC="\033[0m" # No Color - -format_ip_for_url() { - local ip="$1" - if echo "$ip" | grep -q ':'; then - # IPv6 - echo "[${ip}]" - else - # IPv4 - echo "${ip}" +install_dokploy() { + if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" >&2 + exit 1 fi + + # check if is Mac OS + if [ "$(uname)" = "Darwin" ]; then + echo "This script must be run on Linux" >&2 + exit 1 + fi + + + # check if is running inside a container + if [ -f /.dockerenv ]; then + echo "This script must be run on Linux" >&2 + exit 1 + fi + + # check if something is running on port 80 + if ss -tulnp | grep ':80 ' >/dev/null; then + echo "Error: something is already running on port 80" >&2 + exit 1 + fi + + # check if something is running on port 443 + if ss -tulnp | grep ':443 ' >/dev/null; then + echo "Error: something is already running on port 443" >&2 + exit 1 + fi + + command_exists() { + command -v "$@" > /dev/null 2>&1 + } + + if command_exists docker; then + echo "Docker already installed" + else + curl -sSL https://get.docker.com | sh + fi + + docker swarm leave --force 2>/dev/null + advertise_addr=$(curl -s ifconfig.me) + + docker swarm init --advertise-addr $advertise_addr + + echo "Swarm initialized" + + docker network rm -f dokploy-network 2>/dev/null + docker network create --driver overlay --attachable dokploy-network + + echo "Network created" + + mkdir -p /etc/dokploy + + chmod 777 /etc/dokploy + + docker pull dokploy/dokploy:feature + + # Installation + docker service create \ + --name dokploy \ + --replicas 1 \ + --network dokploy-network \ + --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ + --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ + --mount type=volume,source=dokploy-docker-config,target=/root/.docker \ + --publish published=3000,target=3000,mode=host \ + --update-parallelism 1 \ + --update-order stop-first \ + --constraint 'node.role == manager' \ + -e RELEASE_TAG=feature \ + dokploy/dokploy:feature + + GREEN="\033[0;32m" + YELLOW="\033[1;33m" + BLUE="\033[0;34m" + NC="\033[0m" # No Color + + format_ip_for_url() { + local ip="$1" + if echo "$ip" | grep -q ':'; then + # IPv6 + echo "[${ip}]" + else + # IPv4 + echo "${ip}" + fi + } + + formatted_addr=$(format_ip_for_url "$advertise_addr") + echo "" + printf "${GREEN}Congratulations, Dokploy is installed!${NC}\n" + printf "${BLUE}Wait 15 seconds for the server to start${NC}\n" + printf "${YELLOW}Please go to http://${formatted_addr}:3000${NC}\n\n" + echo "" } -formatted_addr=$(format_ip_for_url "$advertise_addr") -echo "" -printf "${GREEN}Congratulations, Dokploy is installed!${NC}\n" -printf "${BLUE}Wait 15 seconds for the server to start${NC}\n" -printf "${YELLOW}Please go to http://${formatted_addr}:3000${NC}\n\n" -echo "" +update_dokploy() { + echo "Updating Dokploy..." + + # Pull the latest feature image + docker pull dokploy/dokploy:feature + + # Update the service + docker service update --image dokploy/dokploy:feature dokploy + + echo "Dokploy has been updated to the latest feature version." +} + +# Main script execution +if [ "$1" = "update" ]; then + update_dokploy +else + install_dokploy +fi \ No newline at end of file From c1c5fc978b5be43067c3bae28d13afaaeacb9ac5 Mon Sep 17 00:00:00 2001 From: AprilNEA Date: Mon, 30 Sep 2024 08:35:49 +0000 Subject: [PATCH 19/32] fix: fix number convert when string empty --- .../advanced/ports/update-port.tsx | 20 ++--------- .../application/domains/add-domain.tsx | 7 ++-- .../dashboard/compose/domains/add-domain.tsx | 7 ++-- apps/dokploy/components/ui/input.tsx | 35 ++++++++++++++++++- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx b/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx index a068ce18..5ad25ed7 100644 --- a/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx @@ -17,7 +17,7 @@ import { FormLabel, FormMessage, } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; +import { Input, NumberInput } from "@/components/ui/input"; import { Select, SelectContent, @@ -125,7 +125,7 @@ export const UpdatePort = ({ portId }: Props) => { Published Port - { if (value === "") { field.onChange(0); } else { - const number = Number.parseInt(value, 10); - if (!Number.isNaN(number)) { - field.onChange(number); - } + field.onChange(parseInt(value, 10)); } }} /> @@ -158,17 +155,6 @@ export const UpdatePort = ({ portId }: Props) => { placeholder="1-65535" {...field} value={field.value?.toString() || ""} - onChange={(e) => { - const value = e.target.value; - if (value === "") { - field.onChange(0); - } else { - const number = Number.parseInt(value, 10); - if (!Number.isNaN(number)) { - field.onChange(number); - } - } - }} /> diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index 7ab67a29..79c54715 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -18,7 +18,7 @@ import { FormLabel, FormMessage, } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; +import { Input, NumberInput } from "@/components/ui/input"; import { Select, SelectContent, @@ -228,12 +228,9 @@ export const AddDomain = ({ Container Port - { - field.onChange(Number.parseInt(e.target.value)); - }} /> diff --git a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx index 91f211d1..e3526db5 100644 --- a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx @@ -18,7 +18,7 @@ import { FormLabel, FormMessage, } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; +import { Input, NumberInput } from "@/components/ui/input"; import { Select, SelectContent, @@ -364,12 +364,9 @@ export const AddDomainCompose = ({ Container Port - { - field.onChange(Number.parseInt(e.target.value)); - }} /> diff --git a/apps/dokploy/components/ui/input.tsx b/apps/dokploy/components/ui/input.tsx index 55b46e6d..d8b94c79 100644 --- a/apps/dokploy/components/ui/input.tsx +++ b/apps/dokploy/components/ui/input.tsx @@ -31,4 +31,37 @@ const Input = React.forwardRef( ); Input.displayName = "Input"; -export { Input }; +const NumberInput = React.forwardRef( + ({ className, errorMessage, ...props }, ref) => { + return ( + { + const value = e.target.value; + if (value === "") { + props.onChange?.(e); + } else { + const number = Number.parseInt(value, 10); + if (!Number.isNaN(number)) { + const syntheticEvent = { + ...e, + target: { + ...e.target, + value: number.toString(), + }, + }; + props.onChange?.(syntheticEvent as React.ChangeEvent); + } + } + }} + /> + ); + } +); +NumberInput.displayName = "NumberInput"; + + +export { Input, NumberInput }; From 5c225c8d42c5f6e62fa487d888e77d0f3e6d7ee1 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:00:01 -0600 Subject: [PATCH 20/32] fix(modal): close the modal after the creation #509 --- apps/dokploy/components/dashboard/project/add-compose.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-compose.tsx b/apps/dokploy/components/dashboard/project/add-compose.tsx index 658bf0ae..91dba943 100644 --- a/apps/dokploy/components/dashboard/project/add-compose.tsx +++ b/apps/dokploy/components/dashboard/project/add-compose.tsx @@ -39,7 +39,7 @@ import { slugify } from "@/lib/slug"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { CircuitBoard, HelpCircle } from "lucide-react"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; @@ -71,6 +71,7 @@ interface Props { export const AddCompose = ({ projectId, projectName }: Props) => { const utils = api.useUtils(); + const [visible, setVisible] = useState(false); const slug = slugify(projectName); const { data: servers } = api.server.withSSHKey.useQuery(); const { mutateAsync, isLoading, error, isError } = @@ -101,6 +102,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => { }) .then(async () => { toast.success("Compose Created"); + setVisible(false); await utils.project.one.invalidate({ projectId, }); @@ -111,7 +113,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => { }; return ( - + Date: Mon, 30 Sep 2024 15:00:32 -0600 Subject: [PATCH 21/32] fix(multi-server): show the servers ip instead of the main ip #502 --- .../mariadb/general/show-external-mariadb-credentials.tsx | 5 +++-- .../mongo/general/show-external-mongo-credentials.tsx | 6 +++--- .../mysql/general/show-external-mysql-credentials.tsx | 6 +++--- .../postgres/general/show-external-postgres-credentials.tsx | 6 +++--- .../redis/general/show-external-redis-credentials.tsx | 5 +++-- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx b/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx index a908de07..c06cacaa 100644 --- a/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx +++ b/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx @@ -48,6 +48,7 @@ export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => { const { data, refetch } = api.mariadb.one.useQuery({ mariadbId }); const { mutateAsync, isLoading } = api.mariadb.saveExternalPort.useMutation(); const [connectionUrl, setConnectionUrl] = useState(""); + const getIp = data?.server?.ipAddress || ip; const form = useForm({ defaultValues: {}, resolver: zodResolver(DockerProviderSchema), @@ -79,7 +80,7 @@ export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => { const buildConnectionUrl = () => { const port = form.watch("externalPort") || data?.externalPort; - return `mariadb://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}/${data?.databaseName}`; + return `mariadb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; }; setConnectionUrl(buildConnectionUrl()); @@ -90,7 +91,7 @@ export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => { form, data?.databaseName, data?.databaseUser, - ip, + getIp, ]); return ( <> diff --git a/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx b/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx index 36d04c9c..7cfab289 100644 --- a/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx +++ b/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx @@ -48,7 +48,7 @@ export const ShowExternalMongoCredentials = ({ mongoId }: Props) => { const { data, refetch } = api.mongo.one.useQuery({ mongoId }); const { mutateAsync, isLoading } = api.mongo.saveExternalPort.useMutation(); const [connectionUrl, setConnectionUrl] = useState(""); - + const getIp = data?.server?.ipAddress || ip; const form = useForm({ defaultValues: {}, resolver: zodResolver(DockerProviderSchema), @@ -80,7 +80,7 @@ export const ShowExternalMongoCredentials = ({ mongoId }: Props) => { const buildConnectionUrl = () => { const port = form.watch("externalPort") || data?.externalPort; - return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}`; + return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}`; }; setConnectionUrl(buildConnectionUrl()); @@ -90,7 +90,7 @@ export const ShowExternalMongoCredentials = ({ mongoId }: Props) => { data?.databasePassword, form, data?.databaseUser, - ip, + getIp, ]); return ( diff --git a/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx b/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx index 18c1adaf..009c8c3a 100644 --- a/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx +++ b/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx @@ -48,7 +48,7 @@ export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => { const { data, refetch } = api.mysql.one.useQuery({ mysqlId }); const { mutateAsync, isLoading } = api.mysql.saveExternalPort.useMutation(); const [connectionUrl, setConnectionUrl] = useState(""); - + const getIp = data?.server?.ipAddress || ip; const form = useForm({ defaultValues: {}, resolver: zodResolver(DockerProviderSchema), @@ -80,7 +80,7 @@ export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => { const buildConnectionUrl = () => { const port = form.watch("externalPort") || data?.externalPort; - return `mysql://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}/${data?.databaseName}`; + return `mysql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; }; setConnectionUrl(buildConnectionUrl()); @@ -91,7 +91,7 @@ export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => { data?.databaseName, data?.databaseUser, form, - ip, + getIp, ]); return ( <> diff --git a/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx b/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx index 28a96eb2..e1b4369a 100644 --- a/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx +++ b/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx @@ -48,6 +48,7 @@ export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => { const { data, refetch } = api.postgres.one.useQuery({ postgresId }); const { mutateAsync, isLoading } = api.postgres.saveExternalPort.useMutation(); + const getIp = data?.server?.ipAddress || ip; const [connectionUrl, setConnectionUrl] = useState(""); const form = useForm({ @@ -79,10 +80,9 @@ export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => { useEffect(() => { const buildConnectionUrl = () => { - const hostname = window.location.hostname; const port = form.watch("externalPort") || data?.externalPort; - return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}/${data?.databaseName}`; + return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; }; setConnectionUrl(buildConnectionUrl()); @@ -92,7 +92,7 @@ export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => { data?.databasePassword, form, data?.databaseName, - ip, + getIp, ]); return ( diff --git a/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx b/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx index b8832841..bcb26267 100644 --- a/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx +++ b/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx @@ -48,6 +48,7 @@ export const ShowExternalRedisCredentials = ({ redisId }: Props) => { const { data, refetch } = api.redis.one.useQuery({ redisId }); const { mutateAsync, isLoading } = api.redis.saveExternalPort.useMutation(); const [connectionUrl, setConnectionUrl] = useState(""); + const getIp = data?.server?.ipAddress || ip; const form = useForm({ defaultValues: {}, @@ -81,11 +82,11 @@ export const ShowExternalRedisCredentials = ({ redisId }: Props) => { const hostname = window.location.hostname; const port = form.watch("externalPort") || data?.externalPort; - return `redis://default:${data?.databasePassword}@${ip}:${port}`; + return `redis://default:${data?.databasePassword}@${getIp}:${port}`; }; setConnectionUrl(buildConnectionUrl()); - }, [data?.appName, data?.externalPort, data?.databasePassword, form, ip]); + }, [data?.appName, data?.externalPort, data?.databasePassword, form, getIp]); return ( <>
    From fc38a425878ea5fe686c7989fcd7fa1bce46fd05 Mon Sep 17 00:00:00 2001 From: AprilNEA Date: Tue, 1 Oct 2024 14:36:45 +0000 Subject: [PATCH 22/32] fix: convert final value --- apps/dokploy/components/ui/input.tsx | 39 +++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/apps/dokploy/components/ui/input.tsx b/apps/dokploy/components/ui/input.tsx index d8b94c79..4a537cef 100644 --- a/apps/dokploy/components/ui/input.tsx +++ b/apps/dokploy/components/ui/input.tsx @@ -39,24 +39,27 @@ const NumberInput = React.forwardRef( className={cn("text-left", className)} ref={ref} {...props} - onChange={(e) => { - const value = e.target.value; - if (value === "") { - props.onChange?.(e); - } else { - const number = Number.parseInt(value, 10); - if (!Number.isNaN(number)) { - const syntheticEvent = { - ...e, - target: { - ...e.target, - value: number.toString(), - }, - }; - props.onChange?.(syntheticEvent as React.ChangeEvent); - } - } - }} + value={props.value === undefined ? undefined : String(props.value)} + onChange={(e) => { + const value = e.target.value; + if (value === "") { + props.onChange?.(e); + } else { + const number = Number.parseInt(value, 10); + if (!Number.isNaN(number)) { + const syntheticEvent = { + ...e, + target: { + ...e.target, + value: number, + }, + }; + props.onChange?.( + syntheticEvent as unknown as React.ChangeEvent + ); + } + } + }} /> ); } From 73bf5274f52f6bc16d207039be03753b5da68ba3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:28:11 -0600 Subject: [PATCH 23/32] chore(version): bump version --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index bb79865e..031e6728 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.9.3", + "version": "v0.9.4", "private": true, "license": "Apache-2.0", "type": "module", From dfd6764320cc1ebe6937fac4a3b0745028822349 Mon Sep 17 00:00:00 2001 From: AprilNEA Date: Wed, 2 Oct 2024 18:22:21 +0000 Subject: [PATCH 24/32] styles: format code with prettier --- .../advanced/ports/update-port.tsx | 2 +- .../application/domains/add-domain.tsx | 5 +-- .../dashboard/compose/domains/add-domain.tsx | 5 +-- apps/dokploy/components/ui/input.tsx | 43 +++++++++---------- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx b/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx index 5ad25ed7..bd8135cb 100644 --- a/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx @@ -134,7 +134,7 @@ export const UpdatePort = ({ portId }: Props) => { if (value === "") { field.onChange(0); } else { - field.onChange(parseInt(value, 10)); + field.onChange(Number.parseInt(value, 10)); } }} /> diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index 79c54715..4b5d4e09 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -228,10 +228,7 @@ export const AddDomain = ({ Container Port - + diff --git a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx index e3526db5..9f586467 100644 --- a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx @@ -364,10 +364,7 @@ export const AddDomainCompose = ({ Container Port - + diff --git a/apps/dokploy/components/ui/input.tsx b/apps/dokploy/components/ui/input.tsx index 4a537cef..8fe7ab28 100644 --- a/apps/dokploy/components/ui/input.tsx +++ b/apps/dokploy/components/ui/input.tsx @@ -40,31 +40,30 @@ const NumberInput = React.forwardRef( ref={ref} {...props} value={props.value === undefined ? undefined : String(props.value)} - onChange={(e) => { - const value = e.target.value; - if (value === "") { - props.onChange?.(e); - } else { - const number = Number.parseInt(value, 10); - if (!Number.isNaN(number)) { - const syntheticEvent = { - ...e, - target: { - ...e.target, - value: number, - }, - }; - props.onChange?.( - syntheticEvent as unknown as React.ChangeEvent - ); - } - } - }} + onChange={(e) => { + const value = e.target.value; + if (value === "") { + props.onChange?.(e); + } else { + const number = Number.parseInt(value, 10); + if (!Number.isNaN(number)) { + const syntheticEvent = { + ...e, + target: { + ...e.target, + value: number, + }, + }; + props.onChange?.( + syntheticEvent as unknown as React.ChangeEvent, + ); + } + } + }} /> ); - } + }, ); NumberInput.displayName = "NumberInput"; - export { Input, NumberInput }; From ecfca9419a5de78eec28fa23f17eeea6b1a96712 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:02:20 -0600 Subject: [PATCH 25/32] refactor: remove innecessary conversion --- .../advanced/ports/update-port.tsx | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx b/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx index bd8135cb..0ed9d2e2 100644 --- a/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx @@ -125,25 +125,14 @@ export const UpdatePort = ({ portId }: Props) => { Published Port - { - const value = e.target.value; - if (value === "") { - field.onChange(0); - } else { - field.onChange(Number.parseInt(value, 10)); - } - }} - /> + )} /> + { Target Port - + From 9745d12ac873f2eb0ea6c4411bc6bda8fb819f20 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 3 Oct 2024 00:48:00 -0600 Subject: [PATCH 26/32] fix(destinations): change admin to protected procedure --- apps/dokploy/server/api/routers/destination.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/server/api/routers/destination.ts b/apps/dokploy/server/api/routers/destination.ts index 6fcfa99b..b7bc9906 100644 --- a/apps/dokploy/server/api/routers/destination.ts +++ b/apps/dokploy/server/api/routers/destination.ts @@ -68,7 +68,7 @@ export const destinationRouter = createTRPCRouter({ const destination = await findDestinationById(input.destinationId); return destination; }), - all: adminProcedure.query(async () => { + all: protectedProcedure.query(async () => { return await db.query.destinations.findMany({}); }), remove: adminProcedure From a5eb4b0a7251f4fae6ea7a46e56e2f6d6a834dd8 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 3 Oct 2024 01:00:35 -0600 Subject: [PATCH 27/32] fix(compose): delete content when is remote server --- apps/dokploy/server/utils/providers/bitbucket.ts | 10 +++++----- apps/dokploy/server/utils/providers/github.ts | 10 +++++----- apps/dokploy/server/utils/providers/gitlab.ts | 10 +++++----- apps/dokploy/server/utils/providers/raw.ts | 1 + 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/dokploy/server/utils/providers/bitbucket.ts b/apps/dokploy/server/utils/providers/bitbucket.ts index c6eb0bbd..3cbb4125 100644 --- a/apps/dokploy/server/utils/providers/bitbucket.ts +++ b/apps/dokploy/server/utils/providers/bitbucket.ts @@ -147,15 +147,15 @@ export const cloneRawBitbucketRepositoryRemote = async (compose: Compose) => { const bitbucketProvider = await findBitbucketById(bitbucketId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`; const cloneUrl = `https://${bitbucketProvider?.bitbucketUsername}:${bitbucketProvider?.appPassword}@${repoclone}`; try { - await execAsyncRemote( - serverId, - `git clone --branch ${bitbucketBranch} --depth 1 ${cloneUrl} ${outputPath}`, - ); + const command = ` + rm -rf ${outputPath}; + git clone --branch ${bitbucketBranch} --depth 1 ${cloneUrl} ${outputPath} + `; + await execAsyncRemote(serverId, command); } catch (error) { throw error; } diff --git a/apps/dokploy/server/utils/providers/github.ts b/apps/dokploy/server/utils/providers/github.ts index 68a401b2..e828d010 100644 --- a/apps/dokploy/server/utils/providers/github.ts +++ b/apps/dokploy/server/utils/providers/github.ts @@ -271,13 +271,13 @@ export const cloneRawGithubRepositoryRemote = async (compose: Compose) => { const octokit = authGithub(githubProvider); const token = await getGithubToken(octokit); const repoclone = `github.com/${owner}/${repository}.git`; - await recreateDirectory(outputPath); const cloneUrl = `https://oauth2:${token}@${repoclone}`; try { - await execAsyncRemote( - serverId, - `git clone --branch ${branch} --depth 1 ${cloneUrl} ${outputPath}`, - ); + const command = ` + rm -rf ${outputPath}; + git clone --branch ${branch} --depth 1 ${cloneUrl} ${outputPath} + `; + await execAsyncRemote(serverId, command); } catch (error) { throw error; } diff --git a/apps/dokploy/server/utils/providers/gitlab.ts b/apps/dokploy/server/utils/providers/gitlab.ts index 95649f5d..43f868d4 100644 --- a/apps/dokploy/server/utils/providers/gitlab.ts +++ b/apps/dokploy/server/utils/providers/gitlab.ts @@ -390,14 +390,14 @@ export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => { await refreshGitlabToken(gitlabId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); const repoclone = `gitlab.com/${gitlabPathNamespace}.git`; const cloneUrl = `https://oauth2:${gitlabProvider?.accessToken}@${repoclone}`; try { - await execAsyncRemote( - serverId, - `git clone --branch ${branch} --depth 1 ${cloneUrl} ${outputPath}`, - ); + const command = ` + rm -rf ${outputPath}; + git clone --branch ${branch} --depth 1 ${cloneUrl} ${outputPath} + `; + await execAsyncRemote(serverId, command); } catch (error) { throw error; } diff --git a/apps/dokploy/server/utils/providers/raw.ts b/apps/dokploy/server/utils/providers/raw.ts index f0fdcb5c..238216fa 100644 --- a/apps/dokploy/server/utils/providers/raw.ts +++ b/apps/dokploy/server/utils/providers/raw.ts @@ -70,6 +70,7 @@ export const createComposeFileRawRemote = async (compose: Compose) => { try { const encodedContent = encodeBase64(composeFile); const command = ` + rm -rf ${outputPath}; mkdir -p ${outputPath}; echo "${encodedContent}" | base64 -d > "${filePath}"; `; From a36518a8f0eb0b8ae2efd989b8732bb2de09160c Mon Sep 17 00:00:00 2001 From: seppulcro Date: Thu, 3 Oct 2024 08:09:21 +0100 Subject: [PATCH 28/32] fix: set static docker image version --- apps/dokploy/templates/roundcube/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/roundcube/docker-compose.yml b/apps/dokploy/templates/roundcube/docker-compose.yml index 4dd6097a..440f907d 100644 --- a/apps/dokploy/templates/roundcube/docker-compose.yml +++ b/apps/dokploy/templates/roundcube/docker-compose.yml @@ -1,6 +1,6 @@ services: roundcubemail: - image: roundcube/roundcubemail:latest + image: roundcube/roundcubemail:1.6.9-apache volumes: - ./www:/var/www/html - ./db/sqlite:/var/roundcube/db From 9b401059b031787ca5f776de44e1ee169244d865 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 3 Oct 2024 01:56:50 -0600 Subject: [PATCH 29/32] fix(registry): add option to login the registry in the remote server --- .../cluster/registry/add-docker-registry.tsx | 118 +- .../registry/update-docker-registry.tsx | 75 +- apps/dokploy/drizzle/0038_rapid_landau.sql | 1 + apps/dokploy/drizzle/meta/0038_snapshot.json | 3824 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/server/api/routers/registry.ts | 25 +- apps/dokploy/server/api/services/registry.ts | 18 +- apps/dokploy/server/db/schema/registry.ts | 11 +- 8 files changed, 4025 insertions(+), 54 deletions(-) create mode 100644 apps/dokploy/drizzle/0038_rapid_landau.sql create mode 100644 apps/dokploy/drizzle/meta/0038_snapshot.json diff --git a/apps/dokploy/components/dashboard/settings/cluster/registry/add-docker-registry.tsx b/apps/dokploy/components/dashboard/settings/cluster/registry/add-docker-registry.tsx index 193bf672..0a1ee614 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/registry/add-docker-registry.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/registry/add-docker-registry.tsx @@ -17,10 +17,18 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { AlertTriangle, Container } from "lucide-react"; -import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -36,10 +44,9 @@ const AddRegistrySchema = z.object({ password: z.string().min(1, { message: "Password is required", }), - registryUrl: z.string().min(1, { - message: "Registry URL is required", - }), + registryUrl: z.string(), imagePrefix: z.string(), + serverId: z.string().optional(), }); type AddRegistry = z.infer; @@ -48,9 +55,9 @@ export const AddRegistry = () => { const utils = api.useUtils(); const [isOpen, setIsOpen] = useState(false); const { mutateAsync, error, isError } = api.registry.create.useMutation(); + const { data: servers } = api.server.withSSHKey.useQuery(); const { mutateAsync: testRegistry, isLoading } = api.registry.testRegistry.useMutation(); - const router = useRouter(); const form = useForm({ defaultValues: { username: "", @@ -58,6 +65,7 @@ export const AddRegistry = () => { registryUrl: "", imagePrefix: "", registryName: "", + serverId: "", }, resolver: zodResolver(AddRegistrySchema), }); @@ -67,6 +75,7 @@ export const AddRegistry = () => { const registryUrl = form.watch("registryUrl"); const registryName = form.watch("registryName"); const imagePrefix = form.watch("imagePrefix"); + const serverId = form.watch("serverId"); useEffect(() => { form.reset({ @@ -74,6 +83,7 @@ export const AddRegistry = () => { password: "", registryUrl: "", imagePrefix: "", + serverId: "", }); }, [form, form.reset, form.formState.isSubmitSuccessful]); @@ -85,6 +95,7 @@ export const AddRegistry = () => { registryUrl: data.registryUrl, registryType: "cloud", imagePrefix: data.imagePrefix, + serverId: data.serverId, }) .then(async (data) => { await utils.registry.all.invalidate(); @@ -211,34 +222,77 @@ export const AddRegistry = () => { )} />
    - - + .then((data) => { + if (data) { + toast.success("Registry Tested Successfully"); + } else { + toast.error("Registry Test Failed"); + } + }) + .catch(() => { + toast.error("Error to test the registry"); + }); + }} + > + Test Registry + + + diff --git a/apps/dokploy/components/dashboard/settings/cluster/registry/update-docker-registry.tsx b/apps/dokploy/components/dashboard/settings/cluster/registry/update-docker-registry.tsx index c84c019a..9c5f281e 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/registry/update-docker-registry.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/registry/update-docker-registry.tsx @@ -17,6 +17,15 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -34,10 +43,9 @@ const updateRegistry = z.object({ message: "Username is required", }), password: z.string(), - registryUrl: z.string().min(1, { - message: "Registry URL is required", - }), + registryUrl: z.string(), imagePrefix: z.string(), + serverId: z.string().optional(), }); type UpdateRegistry = z.infer; @@ -48,6 +56,8 @@ interface Props { export const UpdateDockerRegistry = ({ registryId }: Props) => { const utils = api.useUtils(); + const { data: servers } = api.server.withSSHKey.useQuery(); + const { mutateAsync: testRegistry, isLoading } = api.registry.testRegistry.useMutation(); const { data, refetch } = api.registry.one.useQuery( @@ -69,15 +79,19 @@ export const UpdateDockerRegistry = ({ registryId }: Props) => { username: "", password: "", registryUrl: "", + serverId: "", }, resolver: zodResolver(updateRegistry), }); + console.log(form.formState.errors); + const password = form.watch("password"); const username = form.watch("username"); const registryUrl = form.watch("registryUrl"); const registryName = form.watch("registryName"); const imagePrefix = form.watch("imagePrefix"); + const serverId = form.watch("serverId"); useEffect(() => { if (data) { @@ -87,6 +101,7 @@ export const UpdateDockerRegistry = ({ registryId }: Props) => { username: data.username || "", password: "", registryUrl: data.registryUrl || "", + serverId: "", }); } }, [form, form.reset, data]); @@ -99,6 +114,7 @@ export const UpdateDockerRegistry = ({ registryId }: Props) => { username: data.username, registryUrl: data.registryUrl, imagePrefix: data.imagePrefix, + serverId: data.serverId, }) .then(async (data) => { toast.success("Registry Updated"); @@ -224,13 +240,47 @@ export const UpdateDockerRegistry = ({ registryId }: Props) => { - - {isCloud && ( + +
    + + Select a server to test the registry. If you don't have a server + choose the default one. + + ( + + Server (Optional) + + + + + + + )} + /> - )} +
    diff --git a/apps/dokploy/drizzle/0038_rapid_landau.sql b/apps/dokploy/drizzle/0038_rapid_landau.sql new file mode 100644 index 00000000..4f756c7c --- /dev/null +++ b/apps/dokploy/drizzle/0038_rapid_landau.sql @@ -0,0 +1 @@ +ALTER TABLE "registry" ALTER COLUMN "registryUrl" SET DEFAULT ''; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0038_snapshot.json b/apps/dokploy/drizzle/meta/0038_snapshot.json new file mode 100644 index 00000000..4eb2b5ec --- /dev/null +++ b/apps/dokploy/drizzle/meta/0038_snapshot.json @@ -0,0 +1,3824 @@ +{ + "id": "8ffdfaff-f166-42dc-ac77-4fd9309d736a", + "prevId": "19a70a39-f719-400b-b61e-6ddf1bcc6ac5", + "version": "6", + "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 + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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 + }, + "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'" + }, + "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" + ] + } + } + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "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": { + "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" + ] + } + } + }, + "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": {} + }, + "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 + } + }, + "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": {} + }, + "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 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + }, + "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 + } + }, + "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": {} + }, + "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 + }, + "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" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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" + ] + } + } + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": { + "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" + ] + } + } + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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" + ] + } + } + }, + "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": {} + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "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": {} + }, + "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 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "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" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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": {} + }, + "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 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + } + }, + "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": {} + }, + "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": {} + }, + "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" + ] + } + } + }, + "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": {} + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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" + ] + } + } + }, + "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 + }, + "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": {} + }, + "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": {} + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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": {} + }, + "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 + }, + "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 + } + }, + "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" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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": {} + }, + "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": {} + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "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 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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 + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_authId_auth_id_fk": { + "name": "git_provider_authId_auth_id_fk", + "tableFrom": "git_provider", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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": {} + }, + "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": {} + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "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": {} + }, + "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 + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "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": {} + } + }, + "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" + ] + }, + "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" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + } + }, + "schemas": {}, + "_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 0592acd4..5a84e0b1 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -267,6 +267,13 @@ "when": 1726988289562, "tag": "0037_legal_namor", "breakpoints": true + }, + { + "idx": 38, + "version": "6", + "when": 1727942090102, + "tag": "0038_rapid_landau", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 8fd8d2ce..ca132966 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -7,7 +7,7 @@ import { apiUpdateRegistry, } from "@/server/db/schema"; import { initializeRegistry } from "@/server/setup/registry-setup"; -import { execAsync } from "@/server/utils/process/execAsync"; +import { execAsync, execAsyncRemote } from "@/server/utils/process/execAsync"; import { manageRegistry } from "@/server/utils/traefik/registry"; import { TRPCError } from "@trpc/server"; import { @@ -58,7 +58,13 @@ export const registryRouter = createTRPCRouter({ .mutation(async ({ input }) => { try { const loginCommand = `echo ${input.password} | docker login ${input.registryUrl} --username ${input.username} --password-stdin`; - await execAsync(loginCommand); + + if (input.serverId && input.serverId !== "none") { + await execAsyncRemote(input.serverId, loginCommand); + } else { + await execAsync(loginCommand); + } + return true; } catch (error) { console.log("Error Registry:", error); @@ -78,6 +84,7 @@ export const registryRouter = createTRPCRouter({ ? input.registryUrl : "dokploy-registry.docker.localhost", imagePrefix: null, + serverId: undefined, }); await manageRegistry(selfHostedRegistry); @@ -86,3 +93,17 @@ export const registryRouter = createTRPCRouter({ return selfHostedRegistry; }), }); + +const shellEscape = (str: string) => { + const ret = []; + let s = str; + if (/[^A-Za-z0-9_\/:=-]/.test(s)) { + s = `'${s.replace(/'/g, "'\\''")}'`; + s = s + .replace(/^(?:'')+/g, "") // unduplicate single-quote at the beginning + .replace(/\\'''/g, "\\'"); // remove non-escaped single-quote if there are enclosed between 2 escaped + } + ret.push(s); + + return ret.join(" "); +}; diff --git a/apps/dokploy/server/api/services/registry.ts b/apps/dokploy/server/api/services/registry.ts index 83dcc2a2..f7c20af6 100644 --- a/apps/dokploy/server/api/services/registry.ts +++ b/apps/dokploy/server/api/services/registry.ts @@ -2,7 +2,7 @@ import { db } from "@/server/db"; import { type apiCreateRegistry, registry } from "@/server/db/schema"; import { initializeRegistry } from "@/server/setup/registry-setup"; import { removeService } from "@/server/utils/docker/utils"; -import { execAsync } from "@/server/utils/process/execAsync"; +import { execAsync, execAsyncRemote } from "@/server/utils/process/execAsync"; import { manageRegistry, removeSelfHostedRegistry, @@ -32,9 +32,10 @@ export const createRegistry = async (input: typeof apiCreateRegistry._type) => { message: "Error input: Inserting registry", }); } - - if (newRegistry.registryType === "cloud") { - const loginCommand = `echo ${input.password} | docker login ${input.registryUrl} --username ${input.username} --password-stdin`; + const loginCommand = `echo ${input.password} | docker login ${input.registryUrl} --username ${input.username} --password-stdin`; + if (input.serverId && input.serverId !== "none") { + await execAsyncRemote(input.serverId, loginCommand); + } else if (newRegistry.registryType === "cloud") { await execAsync(loginCommand); } @@ -76,7 +77,7 @@ export const removeRegistry = async (registryId: string) => { export const updateRegistry = async ( registryId: string, - registryData: Partial, + registryData: Partial & { serverId?: string | null }, ) => { try { const response = await db @@ -92,6 +93,13 @@ export const updateRegistry = async ( await manageRegistry(response); await initializeRegistry(response.username, response.password); } + const loginCommand = `echo ${response?.password} | docker login ${response?.registryUrl} --username ${response?.username} --password-stdin`; + + if (registryData?.serverId && registryData?.serverId !== "none") { + await execAsyncRemote(registryData.serverId, loginCommand); + } else if (response?.registryType === "cloud") { + await execAsync(loginCommand); + } return response; } catch (error) { diff --git a/apps/dokploy/server/db/schema/registry.ts b/apps/dokploy/server/db/schema/registry.ts index 832de9b1..ee1bab94 100644 --- a/apps/dokploy/server/db/schema/registry.ts +++ b/apps/dokploy/server/db/schema/registry.ts @@ -23,7 +23,7 @@ export const registry = pgTable("registry", { imagePrefix: text("imagePrefix"), username: text("username").notNull(), password: text("password").notNull(), - registryUrl: text("registryUrl").notNull(), + registryUrl: text("registryUrl").notNull().default(""), createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), @@ -45,7 +45,7 @@ const createSchema = createInsertSchema(registry, { registryName: z.string().min(1), username: z.string().min(1), password: z.string().min(1), - registryUrl: z.string().min(1), + registryUrl: z.string(), adminId: z.string().min(1), registryId: z.string().min(1), registryType: z.enum(["selfHosted", "cloud"]), @@ -62,7 +62,10 @@ export const apiCreateRegistry = createSchema registryType: z.enum(["selfHosted", "cloud"]), imagePrefix: z.string().nullable().optional(), }) - .required(); + .required() + .extend({ + serverId: z.string().optional(), + }); export const apiTestRegistry = createSchema.pick({}).extend({ registryName: z.string().min(1), @@ -71,6 +74,7 @@ export const apiTestRegistry = createSchema.pick({}).extend({ registryUrl: z.string(), registryType: z.enum(["selfHosted", "cloud"]), imagePrefix: z.string().nullable().optional(), + serverId: z.string().optional(), }); export const apiRemoveRegistry = createSchema @@ -87,6 +91,7 @@ export const apiFindOneRegistry = createSchema export const apiUpdateRegistry = createSchema.partial().extend({ registryId: z.string().min(1), + serverId: z.string().optional(), }); export const apiEnableSelfHostedRegistry = createSchema From 682863f83ec91aa7554ca54fa70d7e59dc454f76 Mon Sep 17 00:00:00 2001 From: Lorenzo Migliorero Date: Thu, 3 Oct 2024 13:54:40 +0200 Subject: [PATCH 30/32] fix: repo length --- apps/dokploy/server/utils/providers/bitbucket.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/server/utils/providers/bitbucket.ts b/apps/dokploy/server/utils/providers/bitbucket.ts index c6eb0bbd..50d0fada 100644 --- a/apps/dokploy/server/utils/providers/bitbucket.ts +++ b/apps/dokploy/server/utils/providers/bitbucket.ts @@ -225,7 +225,7 @@ export const getBitbucketRepositories = async (bitbucketId?: string) => { const username = bitbucketProvider.bitbucketWorkspaceName || bitbucketProvider.bitbucketUsername; - const url = `https://api.bitbucket.org/2.0/repositories/${username}`; + const url = `https://api.bitbucket.org/2.0/repositories/${username}?pagelen=100`; try { const response = await fetch(url, { @@ -244,6 +244,8 @@ export const getBitbucketRepositories = async (bitbucketId?: string) => { const data = await response.json(); + console.log(data.values); + const mappedData = data.values.map((repo: any) => { return { name: repo.name, From ca904c15d90ea6c8911af9adc5b204997045d16a Mon Sep 17 00:00:00 2001 From: Lorenzo Migliorero Date: Thu, 3 Oct 2024 13:55:28 +0200 Subject: [PATCH 31/32] remove console.log --- apps/dokploy/server/utils/providers/bitbucket.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/dokploy/server/utils/providers/bitbucket.ts b/apps/dokploy/server/utils/providers/bitbucket.ts index 50d0fada..06411005 100644 --- a/apps/dokploy/server/utils/providers/bitbucket.ts +++ b/apps/dokploy/server/utils/providers/bitbucket.ts @@ -244,8 +244,6 @@ export const getBitbucketRepositories = async (bitbucketId?: string) => { const data = await response.json(); - console.log(data.values); - const mappedData = data.values.map((repo: any) => { return { name: repo.name, From fb72132a4b004c132ec4d94f887223199df98404 Mon Sep 17 00:00:00 2001 From: Lorenzo Migliorero Date: Thu, 3 Oct 2024 14:05:12 +0200 Subject: [PATCH 32/32] remove watch flag --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index bb79865e..62c98f55 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -11,7 +11,7 @@ "build-next": "next build", "setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run", "reset-password": "node dist/reset-password.mjs", - "dev": "tsx watch -r dotenv/config ./server/server.ts --project tsconfig.server.json ", + "dev": "tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ", "studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts", "migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts", "migration:run": "tsx -r dotenv/config migration.ts",