diff --git a/.husky/commit-msg b/.config/.husky/commit-msg similarity index 100% rename from .husky/commit-msg rename to .config/.husky/commit-msg diff --git a/.husky/install.mjs b/.config/.husky/install.mjs similarity index 100% rename from .husky/install.mjs rename to .config/.husky/install.mjs diff --git a/.husky/pre-commit b/.config/.husky/pre-commit similarity index 100% rename from .husky/pre-commit rename to .config/.husky/pre-commit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d04ee676f..2cbf916f5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -166,20 +166,26 @@ import { generateRandomDomain, type Template, type Schema, + type DomainSchema, } from "../utils"; export function generate(schema: Schema): Template { // do your stuff here, like create a new domain, generate random passwords, mounts. const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const secretBase = generateBase64(64); const toptKeyBase = generateBase64(32); + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 8000, + serviceName: "plausible", + }, + ]; + const envs = [ - // If you want to show a domain in the UI, please add the prefix _HOST at the end of the variable name. - `PLAUSIBLE_HOST=${randomDomain}`, - "PLAUSIBLE_PORT=8000", - `BASE_URL=http://${randomDomain}`, + `BASE_URL=http://${mainDomain}`, `SECRET_KEY_BASE=${secretBase}`, `TOTP_VAULT_KEY=${toptKeyBase}`, `HASH=${mainServiceHash}`, @@ -195,6 +201,7 @@ export function generate(schema: Schema): Template { return { envs, mounts, + domains, }; } ``` diff --git a/Dockerfile b/Dockerfile index 40f1dbeef..8da1db459 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,7 @@ RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \ && pnpm install -g tsx # Install buildpacks -RUN curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.35.0/pack-v0.35.0-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack +COPY --from=buildpacksio/pack:0.35.0 /usr/local/bin/pack /usr/local/bin/pack EXPOSE 3000 CMD [ "pnpm", "start" ] diff --git a/LICENSE.MD b/LICENSE.MD new file mode 100644 index 000000000..9031c94b9 --- /dev/null +++ b/LICENSE.MD @@ -0,0 +1,26 @@ +# License + +## Core License (Apache License 2.0) + +Copyright 2024 Mauricio Siu. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and limitations under the License. + +## Additional Terms for Specific Features + +The following additional terms apply to the multi-node support and Docker Compose file support features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License: + +- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support and Docker Compose file support, will always be free to use in the self-hosted version. +- **Restriction on Resale**: The multi-node support and Docker Compose file support features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent. +- **Modification Distribution**: Any modifications to the multi-node support and Docker Compose file support features must be distributed freely and cannot be sold or offered as a service. + +For further inquiries or permissions, please contact us directly. diff --git a/README.md b/README.md index cfec6692f..3577d78e3 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@
Join us on Discord for help, feedback, and discussions!

- + Discord Shield
@@ -70,6 +70,7 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
Lightspeed.run +Lightspeed.run
### Community Backers 🤝 diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts new file mode 100644 index 000000000..9d5c0b52a --- /dev/null +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -0,0 +1,89 @@ +import type { Domain } from "@/server/api/services/domain"; +import { createDomainLabels } from "@/server/utils/docker/domain"; +import { describe, expect, it } from "vitest"; + +describe("createDomainLabels", () => { + const appName = "test-app"; + const baseDomain: Domain = { + host: "example.com", + port: 8080, + https: false, + uniqueConfigKey: 1, + certificateType: "none", + applicationId: "", + composeId: "", + domainType: "compose", + serviceName: "test-app", + domainId: "", + path: "/", + createdAt: "", + }; + + it("should create basic labels for web entrypoint", async () => { + const labels = await createDomainLabels(appName, baseDomain, "web"); + expect(labels).toEqual([ + "traefik.http.routers.test-app-1-web.rule=Host(`example.com`)", + "traefik.http.routers.test-app-1-web.entrypoints=web", + "traefik.http.services.test-app-1-web.loadbalancer.server.port=8080", + "traefik.http.routers.test-app-1-web.service=test-app-1-web", + ]); + }); + + it("should create labels for websecure entrypoint", async () => { + const labels = await createDomainLabels(appName, baseDomain, "websecure"); + expect(labels).toEqual([ + "traefik.http.routers.test-app-1-websecure.rule=Host(`example.com`)", + "traefik.http.routers.test-app-1-websecure.entrypoints=websecure", + "traefik.http.services.test-app-1-websecure.loadbalancer.server.port=8080", + "traefik.http.routers.test-app-1-websecure.service=test-app-1-websecure", + ]); + }); + + it("should add redirect middleware for https on web entrypoint", async () => { + const httpsBaseDomain = { ...baseDomain, https: true }; + const labels = await createDomainLabels(appName, httpsBaseDomain, "web"); + expect(labels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file", + ); + }); + + it("should add Let's Encrypt configuration for websecure with letsencrypt certificate", async () => { + const letsencryptDomain = { + ...baseDomain, + https: true, + certificateType: "letsencrypt" as const, + }; + const labels = await createDomainLabels( + appName, + letsencryptDomain, + "websecure", + ); + expect(labels).toContain( + "traefik.http.routers.test-app-1-websecure.tls.certresolver=letsencrypt", + ); + }); + + it("should not add Let's Encrypt configuration for non-letsencrypt certificate", async () => { + const nonLetsencryptDomain = { + ...baseDomain, + https: true, + certificateType: "none" as const, + }; + const labels = await createDomainLabels( + appName, + nonLetsencryptDomain, + "websecure", + ); + expect(labels).not.toContain( + "traefik.http.routers.test-app-1-websecure.tls.certresolver=letsencrypt", + ); + }); + + it("should handle different ports correctly", async () => { + const customPortDomain = { ...baseDomain, port: 3000 }; + const labels = await createDomainLabels(appName, customPortDomain, "web"); + expect(labels).toContain( + "traefik.http.services.test-app-1-web.loadbalancer.server.port=3000", + ); + }); +}); diff --git a/apps/dokploy/__test__/compose/domain/network-root.test.ts b/apps/dokploy/__test__/compose/domain/network-root.test.ts new file mode 100644 index 000000000..ca8b16550 --- /dev/null +++ b/apps/dokploy/__test__/compose/domain/network-root.test.ts @@ -0,0 +1,29 @@ +import { addDokployNetworkToRoot } from "@/server/utils/docker/domain"; +import { describe, expect, it } from "vitest"; + +describe("addDokployNetworkToRoot", () => { + it("should create network object if networks is undefined", () => { + const result = addDokployNetworkToRoot(undefined); + expect(result).toEqual({ "dokploy-network": { external: true } }); + }); + + it("should add network to an empty object", () => { + const result = addDokployNetworkToRoot({}); + expect(result).toEqual({ "dokploy-network": { external: true } }); + }); + + it("should not modify existing network configuration", () => { + const existing = { "dokploy-network": { external: false } }; + const result = addDokployNetworkToRoot(existing); + expect(result).toEqual({ "dokploy-network": { external: true } }); + }); + + it("should add network alongside existing networks", () => { + const existing = { "other-network": { external: true } }; + const result = addDokployNetworkToRoot(existing); + expect(result).toEqual({ + "other-network": { external: true }, + "dokploy-network": { external: true }, + }); + }); +}); diff --git a/apps/dokploy/__test__/compose/domain/network-service.test.ts b/apps/dokploy/__test__/compose/domain/network-service.test.ts new file mode 100644 index 000000000..18faf5645 --- /dev/null +++ b/apps/dokploy/__test__/compose/domain/network-service.test.ts @@ -0,0 +1,24 @@ +import { addDokployNetworkToService } from "@/server/utils/docker/domain"; +import { describe, expect, it } from "vitest"; + +describe("addDokployNetworkToService", () => { + it("should add network to an empty array", () => { + const result = addDokployNetworkToService([]); + expect(result).toEqual(["dokploy-network"]); + }); + + it("should not add duplicate network to an array", () => { + const result = addDokployNetworkToService(["dokploy-network"]); + expect(result).toEqual(["dokploy-network"]); + }); + + it("should add network to an existing array with other networks", () => { + const result = addDokployNetworkToService(["other-network"]); + expect(result).toEqual(["other-network", "dokploy-network"]); + }); + + it("should add network to an object if networks is an object", () => { + const result = addDokployNetworkToService({ "other-network": {} }); + expect(result).toEqual({ "other-network": {}, "dokploy-network": {} }); + }); +}); diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 5c490f7ce..70dbe5494 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -67,6 +67,9 @@ const baseDomain: Domain = { https: false, path: null, port: null, + serviceName: "", + composeId: "", + domainType: "application", uniqueConfigKey: 1, }; diff --git a/apps/dokploy/components/dashboard/application/advanced/ports/add-port.tsx b/apps/dokploy/components/dashboard/application/advanced/ports/add-port.tsx index 873baa679..1b613704a 100644 --- a/apps/dokploy/components/dashboard/application/advanced/ports/add-port.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/ports/add-port.tsx @@ -28,7 +28,7 @@ import { import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PlusIcon } 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"; @@ -52,6 +52,7 @@ export const AddPort = ({ applicationId, children = , }: Props) => { + const [isOpen, setIsOpen] = useState(false); const utils = api.useUtils(); const { mutateAsync, isLoading, error, isError } = @@ -82,6 +83,7 @@ export const AddPort = ({ await utils.application.one.invalidate({ applicationId, }); + setIsOpen(false); }) .catch(() => { toast.error("Error to create the port"); @@ -89,7 +91,7 @@ export const AddPort = ({ }; return ( - + 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 8f9d9cd7f..a068ce18c 100644 --- a/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/ports/update-port.tsx @@ -28,7 +28,7 @@ import { import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PenBoxIcon, Pencil } 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"; @@ -49,6 +49,7 @@ interface Props { } export const UpdatePort = ({ portId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const utils = api.useUtils(); const { data } = api.port.one.useQuery( { @@ -89,6 +90,7 @@ export const UpdatePort = ({ portId }: Props) => { await utils.application.one.invalidate({ applicationId: response?.applicationId, }); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the port"); @@ -96,7 +98,7 @@ export const UpdatePort = ({ portId }: Props) => { }; return ( - + diff --git a/apps/dokploy/components/dashboard/application/advanced/redirects/update-redirect.tsx b/apps/dokploy/components/dashboard/application/advanced/redirects/update-redirect.tsx index 855f5c8c0..52ff310d2 100644 --- a/apps/dokploy/components/dashboard/application/advanced/redirects/update-redirect.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/redirects/update-redirect.tsx @@ -23,7 +23,7 @@ import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PenBoxIcon, Pencil } 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"; @@ -41,6 +41,7 @@ interface Props { export const UpdateRedirect = ({ redirectId }: Props) => { const utils = api.useUtils(); + const [isOpen, setIsOpen] = useState(false); const { data } = api.redirects.one.useQuery( { redirectId, @@ -84,6 +85,7 @@ export const UpdateRedirect = ({ redirectId }: Props) => { await utils.application.one.invalidate({ applicationId: response?.applicationId, }); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the redirect"); @@ -91,7 +93,7 @@ export const UpdateRedirect = ({ redirectId }: Props) => { }; return ( - + diff --git a/apps/dokploy/components/dashboard/application/advanced/security/update-security.tsx b/apps/dokploy/components/dashboard/application/advanced/security/update-security.tsx index bb6e59aeb..1e5af95ff 100644 --- a/apps/dokploy/components/dashboard/application/advanced/security/update-security.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/security/update-security.tsx @@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PenBoxIcon, Pencil } 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"; @@ -38,6 +38,7 @@ interface Props { } export const UpdateSecurity = ({ securityId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const utils = api.useUtils(); const { data } = api.security.one.useQuery( { @@ -79,6 +80,7 @@ export const UpdateSecurity = ({ securityId }: Props) => { await utils.application.one.invalidate({ applicationId: response?.applicationId, }); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the security"); @@ -86,7 +88,7 @@ export const UpdateSecurity = ({ securityId }: Props) => { }; return ( - + diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx index 4bb6851c2..2d847fbe2 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx @@ -2,6 +2,7 @@ import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Dialog, + DialogClose, DialogContent, DialogDescription, DialogFooter, @@ -22,7 +23,7 @@ import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { Pencil } 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"; @@ -76,6 +77,7 @@ export const UpdateVolume = ({ refetch, serviceType, }: Props) => { + const [isOpen, setIsOpen] = useState(false); const utils = api.useUtils(); const { data } = api.mounts.one.useQuery( { @@ -135,6 +137,7 @@ export const UpdateVolume = ({ }) .then(() => { toast.success("Mount Update"); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the Bind mount"); @@ -148,6 +151,7 @@ export const UpdateVolume = ({ }) .then(() => { toast.success("Mount Update"); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the Volume mount"); @@ -162,6 +166,7 @@ export const UpdateVolume = ({ }) .then(() => { toast.success("Mount Update"); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the File mount"); @@ -171,7 +176,7 @@ export const UpdateVolume = ({ }; return ( - + + + + diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx index 31ed4e2b4..0458bbbb7 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx @@ -53,7 +53,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
Webhook URL:
- + {`${url}/api/deploy/${data?.refreshToken}`} @@ -72,7 +72,7 @@ export const ShowDeployments = ({ applicationId }: Props) => { {deployments?.map((deployment) => (
@@ -87,7 +87,7 @@ export const ShowDeployments = ({ applicationId }: Props) => { {deployment.title} {deployment.description && ( - + {deployment.description} )} diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index 71e44f929..ab7db1ef5 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -27,13 +27,20 @@ import { SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; -import { domain } from "@/server/db/validations"; +import { domain } from "@/server/db/validations/domain"; import { zodResolver } from "@hookform/resolvers/zod"; +import { Dices } from "lucide-react"; import type z from "zod"; type Domain = z.infer; @@ -60,10 +67,22 @@ export const AddDomain = ({ }, ); + const { data: application } = api.application.one.useQuery( + { + applicationId, + }, + { + enabled: !!applicationId, + }, + ); + const { mutateAsync, isError, error, isLoading } = domainId ? api.domain.update.useMutation() : api.domain.create.useMutation(); + const { mutateAsync: generateDomain, isLoading: isLoadingGenerate } = + api.domain.generateDomain.useMutation(); + const form = useForm({ resolver: zodResolver(domain), }); @@ -142,9 +161,42 @@ export const AddDomain = ({ render={({ field }) => ( Host - - - +
+ + + + + + + + + +

Generate traefik.me domain

+
+
+
+
diff --git a/apps/dokploy/components/dashboard/application/domains/delete-domain.tsx b/apps/dokploy/components/dashboard/application/domains/delete-domain.tsx index 63bd3f305..5933a99a6 100644 --- a/apps/dokploy/components/dashboard/application/domains/delete-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/delete-domain.tsx @@ -44,12 +44,19 @@ export const DeleteDomain = ({ domainId }: Props) => { domainId, }) .then((data) => { - utils.domain.byApplicationId.invalidate({ - applicationId: data?.applicationId, - }); - utils.application.readTraefikConfig.invalidate({ - applicationId: data?.applicationId, - }); + if (data?.applicationId) { + utils.domain.byApplicationId.invalidate({ + applicationId: data?.applicationId, + }); + utils.application.readTraefikConfig.invalidate({ + applicationId: data?.applicationId, + }); + } else if (data?.composeId) { + utils.domain.byComposeId.invalidate({ + composeId: data?.composeId, + }); + } + toast.success("Domain delete succesfully"); }) .catch(() => { diff --git a/apps/dokploy/components/dashboard/application/domains/generate-domain.tsx b/apps/dokploy/components/dashboard/application/domains/generate-domain.tsx deleted file mode 100644 index 9ebe8e30c..000000000 --- a/apps/dokploy/components/dashboard/application/domains/generate-domain.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { api } from "@/utils/api"; -import { RefreshCcw } from "lucide-react"; -import Link from "next/link"; -import { GenerateTraefikMe } from "./generate-traefikme"; -import { GenerateWildCard } from "./generate-wildcard"; - -interface Props { - applicationId: string; -} - -export const GenerateDomain = ({ applicationId }: Props) => { - return ( - - - - - - - Generate Domain - - Generate Domains for your applications - - - -
-
    -
  • -
    -
    - 1. Generate TraefikMe Domain -
    -
    - This option generates a free domain provided by{" "} - - TraefikMe - - . We recommend using this for quick domain testing or if you - don't have a domain yet. -
    -
    -
  • - {/*
  • -
    -
    - 2. Use Wildcard Domain -
    -
    - To use this option, you need to set up an 'A' record in your - domain provider. For example, create a record for - *.yourdomain.com. -
    -
    -
  • */} -
-
- - {/* */} -
-
-
-
- ); -}; diff --git a/apps/dokploy/components/dashboard/application/domains/generate-traefikme.tsx b/apps/dokploy/components/dashboard/application/domains/generate-traefikme.tsx deleted file mode 100644 index 3085b3a88..000000000 --- a/apps/dokploy/components/dashboard/application/domains/generate-traefikme.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; -import { Button } from "@/components/ui/button"; -import { api } from "@/utils/api"; -import { RefreshCcw } from "lucide-react"; -import React from "react"; -import { toast } from "sonner"; - -interface Props { - applicationId: string; -} -export const GenerateTraefikMe = ({ applicationId }: Props) => { - const { mutateAsync, isLoading } = api.domain.generateDomain.useMutation(); - const utils = api.useUtils(); - return ( - - - - - - - - Are you sure to generate a new domain? - - - This will generate a new domain and will be used to access to the - application - - - - Cancel - { - await mutateAsync({ - applicationId, - }) - .then((data) => { - utils.domain.byApplicationId.invalidate({ - applicationId: applicationId, - }); - utils.application.readTraefikConfig.invalidate({ - applicationId: applicationId, - }); - toast.success("Generated Domain succesfully"); - }) - .catch(() => { - toast.error("Error to generate Domain"); - }); - }} - > - Confirm - - - - - ); -}; diff --git a/apps/dokploy/components/dashboard/application/domains/generate-wildcard.tsx b/apps/dokploy/components/dashboard/application/domains/generate-wildcard.tsx deleted file mode 100644 index da4445527..000000000 --- a/apps/dokploy/components/dashboard/application/domains/generate-wildcard.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; -import { Button } from "@/components/ui/button"; -import { api } from "@/utils/api"; -import { SquareAsterisk } from "lucide-react"; -import React from "react"; -import { toast } from "sonner"; - -interface Props { - applicationId: string; -} -export const GenerateWildCard = ({ applicationId }: Props) => { - const { mutateAsync, isLoading } = api.domain.generateWildcard.useMutation(); - const utils = api.useUtils(); - return ( - - - - - - - - Are you sure to generate a new wildcard domain? - - - This will generate a new domain and will be used to access to the - application - - - - Cancel - { - await mutateAsync({ - applicationId, - }) - .then((data) => { - utils.domain.byApplicationId.invalidate({ - applicationId: applicationId, - }); - utils.application.readTraefikConfig.invalidate({ - applicationId: applicationId, - }); - toast.success("Generated Domain succesfully"); - }) - .catch((e) => { - toast.error(`Error to generate Domain: ${e.message}`); - }); - }} - > - Confirm - - - - - ); -}; diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx index d7724ce72..46c05f150 100644 --- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx @@ -12,7 +12,6 @@ import { ExternalLink, GlobeIcon, PenBoxIcon } from "lucide-react"; import Link from "next/link"; import { AddDomain } from "./add-domain"; import { DeleteDomain } from "./delete-domain"; -import { GenerateDomain } from "./generate-domain"; interface Props { applicationId: string; @@ -46,9 +45,6 @@ export const ShowDomains = ({ applicationId }: Props) => { )} - {data && data?.length > 0 && ( - - )}
@@ -65,8 +61,6 @@ export const ShowDomains = ({ applicationId }: Props) => { Add Domain - -
) : ( diff --git a/apps/dokploy/components/dashboard/application/update-application.tsx b/apps/dokploy/components/dashboard/application/update-application.tsx index a769804da..f40a00203 100644 --- a/apps/dokploy/components/dashboard/application/update-application.tsx +++ b/apps/dokploy/components/dashboard/application/update-application.tsx @@ -22,7 +22,7 @@ import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { AlertTriangle, SquarePen } 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"; @@ -41,6 +41,7 @@ interface Props { } export const UpdateApplication = ({ applicationId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const utils = api.useUtils(); const { mutateAsync, error, isError, isLoading } = api.application.update.useMutation(); @@ -79,6 +80,7 @@ export const UpdateApplication = ({ applicationId }: Props) => { utils.application.one.invalidate({ applicationId: applicationId, }); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the application"); @@ -87,7 +89,7 @@ export const UpdateApplication = ({ applicationId }: Props) => { }; return ( - + + + +

+ Fetch: Will clone the repository and load the + services +

+
+ + + + + + + + +

+ Cache: If you previously deployed this + compose, it will read the services from the + last deployment/fetch from the repository +

+
+
+
+
+ + + + )} + /> + + + ( + + Host +
+ + + + + + + + + +

Generate traefik.me domain

+
+
+
+
+ + +
+ )} + /> + + { + return ( + + Path + + + + + + ); + }} + /> + + { + return ( + + Container Port + + { + field.onChange(Number.parseInt(e.target.value)); + }} + /> + + + + ); + }} + /> + {https && ( + ( + + Certificate + + + + )} + /> + )} + + ( + +
+ HTTPS + + Automatically provision SSL Certificate. + + +
+ + + +
+ )} + /> + + + + + + + + + +
+ ); +}; diff --git a/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx b/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx new file mode 100644 index 000000000..01e6f1abc --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx @@ -0,0 +1,111 @@ +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { api } from "@/utils/api"; +import { ExternalLink, GlobeIcon, PenBoxIcon } from "lucide-react"; +import Link from "next/link"; +import { DeleteDomain } from "../../application/domains/delete-domain"; +import { AddDomainCompose } from "./add-domain"; + +interface Props { + composeId: string; +} + +export const ShowDomainsCompose = ({ composeId }: Props) => { + const { data } = api.domain.byComposeId.useQuery( + { + composeId, + }, + { + enabled: !!composeId, + }, + ); + + return ( +
+ + +
+ Domains + + Domains are used to access to the application + +
+ +
+ {data && data?.length > 0 && ( + + + + )} +
+
+ + {data?.length === 0 ? ( +
+ + + To access to the application is required to set at least 1 + domain + +
+ + + +
+
+ ) : ( +
+ {data?.map((item) => { + return ( +
+ + + + + + + + +
+ + + + +
+
+ ); + })} +
+ )} +
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx index 035d6c417..bb896e9ac 100644 --- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx +++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx @@ -72,7 +72,7 @@ export const ComposeFileEditor = ({ composeId }: Props) => { .then(async () => { toast.success("Compose config Updated"); refetch(); - await utils.compose.allServices.invalidate({ + await utils.compose.getConvertedCompose.invalidate({ composeId, }); }) diff --git a/apps/dokploy/components/dashboard/compose/general/generic/show.tsx b/apps/dokploy/components/dashboard/compose/general/generic/show.tsx index 2db4248d5..93a2d7698 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/show.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/show.tsx @@ -5,6 +5,7 @@ import { GitBranch, LockIcon } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { ComposeFileEditor } from "../compose-file-editor"; +import { ShowConvertedCompose } from "../show-converted-compose"; import { SaveGitProviderCompose } from "./save-git-provider-compose"; import { SaveGithubProviderCompose } from "./save-github-provider-compose"; @@ -29,7 +30,8 @@ export const ShowProviderFormCompose = ({ composeId }: Props) => { Select the source of your code

-
+
+
diff --git a/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx new file mode 100644 index 000000000..bb01badc5 --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx @@ -0,0 +1,87 @@ +import { AlertBlock } from "@/components/shared/alert-block"; +import { CodeEditor } from "@/components/shared/code-editor"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { api } from "@/utils/api"; +import { Puzzle, RefreshCw } from "lucide-react"; +import { useState } from "react"; +import { toast } from "sonner"; + +interface Props { + composeId: string; +} + +export const ShowConvertedCompose = ({ composeId }: Props) => { + const [isOpen, setIsOpen] = useState(false); + const { + data: compose, + error, + isError, + refetch, + } = api.compose.getConvertedCompose.useQuery( + { composeId }, + { + retry: false, + }, + ); + + const { mutateAsync, isLoading } = api.compose.fetchSourceType.useMutation(); + + return ( + + + + + + + Converted Compose + + Preview your docker-compose file with added domains. Note: At least + one domain must be specified for this conversion to take effect. + + + {isError && {error?.message}} + +
+ +
+ +
+					
+				
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/compose/update-compose.tsx b/apps/dokploy/components/dashboard/compose/update-compose.tsx index 391801792..5991c03da 100644 --- a/apps/dokploy/components/dashboard/compose/update-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/update-compose.tsx @@ -22,7 +22,7 @@ import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { SquarePen } 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"; @@ -41,6 +41,7 @@ interface Props { } export const UpdateCompose = ({ composeId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const utils = api.useUtils(); const { mutateAsync, error, isError, isLoading } = api.compose.update.useMutation(); @@ -79,6 +80,7 @@ export const UpdateCompose = ({ composeId }: Props) => { utils.compose.one.invalidate({ composeId: composeId, }); + setIsOpen(false); }) .catch(() => { toast.error("Error to update the Compose"); @@ -87,7 +89,7 @@ export const UpdateCompose = ({ composeId }: Props) => { }; return ( - +
- {/* { })} > Manage Github App - */} +
) : ( @@ -119,9 +93,9 @@ export const GithubSetup = () => {
Install Github App diff --git a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx index 04d62872f..00119d426 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx @@ -27,7 +27,7 @@ import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { Mail, PenBoxIcon } from "lucide-react"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { FieldErrors, useFieldArray, useForm } from "react-hook-form"; import { toast } from "sonner"; import { @@ -41,6 +41,7 @@ interface Props { export const UpdateNotification = ({ notificationId }: Props) => { const utils = api.useUtils(); + const [isOpen, setIsOpen] = useState(false); const { data, refetch } = api.notification.one.useQuery( { notificationId, @@ -207,6 +208,7 @@ export const UpdateNotification = ({ notificationId }: Props) => { toast.success("Notification Updated"); await utils.notification.all.invalidate(); refetch(); + setIsOpen(false); }) .catch(() => { toast.error("Error to update a notification"); @@ -214,7 +216,7 @@ export const UpdateNotification = ({ notificationId }: Props) => { } }; return ( - + +
+ + )} + /> + + + + + + + + +
+ ); +}; diff --git a/apps/dokploy/components/dashboard/settings/web-server/show-main-traefik-config.tsx b/apps/dokploy/components/dashboard/settings/web-server/show-main-traefik-config.tsx index e6e4866e9..becda3ade 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/show-main-traefik-config.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/show-main-traefik-config.tsx @@ -106,6 +106,7 @@ export const ShowMainTraefikConfig = ({ children }: Props) => { Traefik config { Traefik config { }, ] : []), + ...(user?.canAccessToSSHKeys + ? [ + { + title: "SSH Keys", + label: "", + icon: KeyRound, + href: "/dashboard/settings/ssh-keys", + }, + ] + : []), ]} /> diff --git a/apps/dokploy/components/shared/code-editor.tsx b/apps/dokploy/components/shared/code-editor.tsx index b20ef60a3..c071f542a 100644 --- a/apps/dokploy/components/shared/code-editor.tsx +++ b/apps/dokploy/components/shared/code-editor.tsx @@ -3,6 +3,7 @@ import { json } from "@codemirror/lang-json"; import { yaml } from "@codemirror/lang-yaml"; import { StreamLanguage } from "@codemirror/language"; import { properties } from "@codemirror/legacy-modes/mode/properties"; +import { EditorView } from "@codemirror/view"; import { githubDark, githubLight } from "@uiw/codemirror-theme-github"; import CodeMirror, { type ReactCodeMirrorProps } from "@uiw/react-codemirror"; import { useTheme } from "next-themes"; @@ -10,6 +11,7 @@ interface Props extends ReactCodeMirrorProps { wrapperClassName?: string; disabled?: boolean; language?: "yaml" | "json" | "properties"; + lineWrapping?: boolean; } export const CodeEditor = ({ @@ -36,6 +38,7 @@ export const CodeEditor = ({ : language === "json" ? json() : StreamLanguage.define(properties), + props.lineWrapping ? EditorView.lineWrapping : [], ]} {...props} editable={!props.disabled} diff --git a/apps/dokploy/drizzle/0030_little_kabuki.sql b/apps/dokploy/drizzle/0030_little_kabuki.sql new file mode 100644 index 000000000..79755d055 --- /dev/null +++ b/apps/dokploy/drizzle/0030_little_kabuki.sql @@ -0,0 +1 @@ +ALTER TABLE "user" ADD COLUMN "canAccessToSSHKeys" boolean DEFAULT false NOT NULL; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0031_steep_vulture.sql b/apps/dokploy/drizzle/0031_steep_vulture.sql new file mode 100644 index 000000000..eed3d9449 --- /dev/null +++ b/apps/dokploy/drizzle/0031_steep_vulture.sql @@ -0,0 +1,15 @@ +DO $$ BEGIN + CREATE TYPE "public"."domainType" AS ENUM('compose', 'application'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +ALTER TABLE "domain" ALTER COLUMN "applicationId" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "domain" ADD COLUMN "serviceName" text;--> statement-breakpoint +ALTER TABLE "domain" ADD COLUMN "domainType" "domainType" DEFAULT 'application';--> statement-breakpoint +ALTER TABLE "domain" ADD COLUMN "composeId" text;--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "domain" ADD CONSTRAINT "domain_composeId_compose_composeId_fk" FOREIGN KEY ("composeId") REFERENCES "public"."compose"("composeId") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/apps/dokploy/drizzle/0032_flashy_shadow_king.sql b/apps/dokploy/drizzle/0032_flashy_shadow_king.sql new file mode 100644 index 000000000..afa5389b5 --- /dev/null +++ b/apps/dokploy/drizzle/0032_flashy_shadow_king.sql @@ -0,0 +1 @@ +ALTER TABLE "domain" ALTER COLUMN "port" SET DEFAULT 3000; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0030_snapshot.json b/apps/dokploy/drizzle/meta/0030_snapshot.json new file mode 100644 index 000000000..78a898cc7 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0030_snapshot.json @@ -0,0 +1,3030 @@ +{ + "id": "8dc242b9-6fa9-4372-9ecd-86ae90dd7d0d", + "prevId": "270dbc4c-104d-4630-b5f3-6c02f0afae49", + "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 + }, + "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 + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "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 + }, + "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 + }, + "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": 80 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "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 + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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'" + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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": {} + } + }, + "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", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "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", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email" + ] + } + }, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0031_snapshot.json b/apps/dokploy/drizzle/meta/0031_snapshot.json new file mode 100644 index 000000000..3330e3369 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0031_snapshot.json @@ -0,0 +1,3071 @@ +{ + "id": "86b27966-a431-493d-9cbc-2291e280f8f2", + "prevId": "8dc242b9-6fa9-4372-9ecd-86ae90dd7d0d", + "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 + }, + "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 + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "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 + }, + "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 + }, + "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": 80 + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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'" + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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": {} + } + }, + "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", + "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", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email" + ] + } + }, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0032_snapshot.json b/apps/dokploy/drizzle/meta/0032_snapshot.json new file mode 100644 index 000000000..aed881976 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0032_snapshot.json @@ -0,0 +1,3071 @@ +{ + "id": "ce8a8861-2970-4889-ac2e-3cfe60d12736", + "prevId": "86b27966-a431-493d-9cbc-2291e280f8f2", + "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 + }, + "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 + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "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 + }, + "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 + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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" + } + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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'" + }, + "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 + } + }, + "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" + } + }, + "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 + }, + "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": {} + } + }, + "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", + "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", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email" + ] + } + }, + "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 8536dd94e..5cecad09c 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -211,6 +211,27 @@ "when": 1722578386823, "tag": "0029_colossal_zodiak", "breakpoints": true + }, + { + "idx": 30, + "version": "6", + "when": 1723608499147, + "tag": "0030_little_kabuki", + "breakpoints": true + }, + { + "idx": 31, + "version": "6", + "when": 1723701656243, + "tag": "0031_steep_vulture", + "breakpoints": true + }, + { + "idx": 32, + "version": "6", + "when": 1723705257806, + "tag": "0032_flashy_shadow_king", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 2fadd0a4a..e662e69b9 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.6.2", + "version": "v0.7.2", "private": true, "license": "Apache-2.0", "type": "module", @@ -39,6 +39,7 @@ "@codemirror/lang-yaml": "^6.1.1", "@codemirror/language": "^6.10.1", "@codemirror/legacy-modes": "6.4.0", + "@codemirror/view": "6.29.0", "@dokploy/trpc-openapi": "0.0.4", "@faker-js/faker": "^8.4.1", "@hookform/resolvers": "^3.3.4", diff --git a/apps/dokploy/pages/api/redirect.ts b/apps/dokploy/pages/api/redirect.ts index b03d9b017..785b078cc 100644 --- a/apps/dokploy/pages/api/redirect.ts +++ b/apps/dokploy/pages/api/redirect.ts @@ -35,7 +35,7 @@ export default async function handler( .update(admins) .set({ githubAppId: data.id, - githubAppName: data.name, + githubAppName: data.html_url, githubClientId: data.client_id, githubClientSecret: data.client_secret, githubWebhookSecret: data.webhook_secret, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index 22ef75058..225517e56 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -2,6 +2,7 @@ import { AddCommandCompose } from "@/components/dashboard/compose/advanced/add-c import { ShowVolumesCompose } from "@/components/dashboard/compose/advanced/show-volumes"; import { DeleteCompose } from "@/components/dashboard/compose/delete-compose"; import { ShowDeploymentsCompose } from "@/components/dashboard/compose/deployments/show-deployments-compose"; +import { ShowDomainsCompose } from "@/components/dashboard/compose/domains/show-domains"; import { ShowEnvironmentCompose } from "@/components/dashboard/compose/enviroment/show"; import { ShowGeneralCompose } from "@/components/dashboard/compose/general/show"; import { ShowDockerLogsCompose } from "@/components/dashboard/compose/logs/show"; @@ -34,6 +35,7 @@ type TabState = | "settings" | "advanced" | "deployments" + | "domains" | "monitoring"; const Service = ( @@ -117,12 +119,13 @@ const Service = ( }} >
- + General Environment Monitoring Logs Deployments + Domains Advanced
@@ -168,6 +171,12 @@ const Service = (
+ + +
+ +
+
diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index 619315be9..338ae3db5 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -1,9 +1,12 @@ import { ShowDestinations } from "@/components/dashboard/settings/ssh-keys/show-ssh-keys"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; +import { appRouter } from "@/server/api/root"; import { validateRequest } from "@/server/auth/auth"; +import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; +import superjson from "superjson"; const Page = () => { return ( @@ -26,7 +29,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { user, session } = await validateRequest(ctx.req, ctx.res); - if (!user || user.rol === "user") { + if (!user) { return { redirect: { permanent: true, @@ -34,8 +37,45 @@ export async function getServerSideProps( }, }; } + const { req, res, resolvedUrl } = ctx; + const helpers = createServerSideHelpers({ + router: appRouter, + ctx: { + req: req as any, + res: res as any, + db: null as any, + session: session, + user: user, + }, + transformer: superjson, + }); - return { - props: {}, - }; + try { + await helpers.project.all.prefetch(); + const auth = await helpers.auth.get.fetch(); + + if (auth.rol === "user") { + const user = await helpers.user.byAuthId.fetch({ + authId: auth.id, + }); + + if (!user.canAccessToSSHKeys) { + return { + redirect: { + permanent: true, + destination: "/", + }, + }; + } + } + return { + props: { + trpcState: helpers.dehydrate(), + }, + }; + } catch (error) { + return { + props: {}, + }; + } } diff --git a/apps/dokploy/public/templates/aptabase.svg b/apps/dokploy/public/templates/aptabase.svg new file mode 100644 index 000000000..3cb71ecfa --- /dev/null +++ b/apps/dokploy/public/templates/aptabase.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/apps/dokploy/public/templates/soketi.png b/apps/dokploy/public/templates/soketi.png new file mode 100644 index 000000000..aec5a79a2 Binary files /dev/null and b/apps/dokploy/public/templates/soketi.png differ diff --git a/apps/dokploy/public/templates/supabase.svg b/apps/dokploy/public/templates/supabase.svg new file mode 100644 index 000000000..2b69d42e7 --- /dev/null +++ b/apps/dokploy/public/templates/supabase.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 262c9ac2c..f3b2362fd 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -12,6 +12,7 @@ import { } from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; +import { db } from "../../db"; import { createAdmin, createUser, @@ -33,6 +34,14 @@ export const authRouter = createTRPCRouter({ .input(apiCreateAdmin) .mutation(async ({ ctx, input }) => { try { + const admin = await db.query.admins.findFirst({}); + + if (admin) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Admin already exists", + }); + } const newAdmin = await createAdmin(input); const session = await lucia.createSession(newAdmin.id || "", {}); ctx.res.appendHeader( diff --git a/apps/dokploy/server/api/routers/cluster.ts b/apps/dokploy/server/api/routers/cluster.ts index 6bd71ca9f..7294c9029 100644 --- a/apps/dokploy/server/api/routers/cluster.ts +++ b/apps/dokploy/server/api/routers/cluster.ts @@ -35,14 +35,23 @@ export const clusterRouter = createTRPCRouter({ }), addWorker: protectedProcedure.query(async ({ input }) => { const result = await docker.swarmInspect(); - return `docker swarm join --token ${ - result.JoinTokens.Worker - } ${await getPublicIpWithFallback()}:2377`; + const docker_version = await docker.version(); + + return { + command: `docker swarm join --token ${ + result.JoinTokens.Worker + } ${await getPublicIpWithFallback()}:2377`, + version: docker_version.Version, + }; }), addManager: protectedProcedure.query(async ({ input }) => { const result = await docker.swarmInspect(); - return `docker swarm join --token ${ - result.JoinTokens.Manager - } ${await getPublicIpWithFallback()}:2377`; + const docker_version = await docker.version(); + return { + command: `docker swarm join --token ${ + result.JoinTokens.Manager + } ${await getPublicIpWithFallback()}:2377`, + version: docker_version.Version, + }; }), }); diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 3b8a92f63..fff95bbd5 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -3,6 +3,7 @@ import { db } from "@/server/db"; import { apiCreateCompose, apiCreateComposeByTemplate, + apiFetchServices, apiFindCompose, apiRandomizeCompose, apiUpdateCompose, @@ -15,16 +16,18 @@ import { import { myQueue } from "@/server/queues/queueSetup"; import { createCommand } from "@/server/utils/builders/compose"; import { randomizeComposeFile } from "@/server/utils/docker/compose"; +import { addDomainToCompose, cloneCompose } from "@/server/utils/docker/domain"; import { removeComposeDirectory } from "@/server/utils/filesystem/directory"; import { templates } from "@/templates/templates"; import type { TemplatesKeys } from "@/templates/types/templates-data.type"; import { generatePassword, loadTemplateModule, - readComposeFile, + readTemplateComposeFile, } from "@/templates/utils"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; +import { dump } from "js-yaml"; import _ from "lodash"; import { nanoid } from "nanoid"; import { findAdmin } from "../services/admin"; @@ -38,6 +41,7 @@ import { updateCompose, } from "../services/compose"; import { removeDeploymentsByComposeId } from "../services/deployment"; +import { createDomain, findDomainsByComposeId } from "../services/domain"; import { createMount } from "../services/mount"; import { findProjectById } from "../services/project"; import { addNewService, checkServiceAccess } from "../services/user"; @@ -113,10 +117,25 @@ export const composeRouter = createTRPCRouter({ await cleanQueuesByCompose(input.composeId); }), - allServices: protectedProcedure - .input(apiFindCompose) + loadServices: protectedProcedure + .input(apiFetchServices) .query(async ({ input }) => { - return await loadServices(input.composeId); + return await loadServices(input.composeId, input.type); + }), + fetchSourceType: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input }) => { + try { + const compose = await findComposeById(input.composeId); + await cloneCompose(compose); + return compose.sourceType; + } catch (err) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to fetch source type", + cause: err, + }); + } }), randomizeCompose: protectedProcedure @@ -124,6 +143,17 @@ export const composeRouter = createTRPCRouter({ .mutation(async ({ input }) => { return await randomizeComposeFile(input.composeId, input.prefix); }), + getConvertedCompose: protectedProcedure + .input(apiFindCompose) + .query(async ({ input }) => { + const compose = await findComposeById(input.composeId); + const domains = await findDomainsByComposeId(input.composeId); + + const composeFile = await addDomainToCompose(compose, domains); + return dump(composeFile, { + lineWidth: 1000, + }); + }), deploy: protectedProcedure .input(apiFindCompose) @@ -189,7 +219,7 @@ export const composeRouter = createTRPCRouter({ if (ctx.user.rol === "user") { await checkServiceAccess(ctx.user.authId, input.projectId, "create"); } - const composeFile = await readComposeFile(input.id); + const composeFile = await readTemplateComposeFile(input.id); const generate = await loadTemplateModule(input.id as TemplatesKeys); @@ -206,7 +236,7 @@ export const composeRouter = createTRPCRouter({ const project = await findProjectById(input.projectId); const projectName = slugify(`${project.name} ${input.id}`); - const { envs, mounts } = generate({ + const { envs, mounts, domains } = generate({ serverIp: admin.serverIp, projectName: projectName, }); @@ -214,7 +244,7 @@ export const composeRouter = createTRPCRouter({ const compose = await createComposeByTemplate({ ...input, composeFile: composeFile, - env: envs.join("\n"), + env: envs?.join("\n"), name: input.id, sourceType: "raw", appName: `${projectName}-${generatePassword(6)}`, @@ -237,6 +267,17 @@ export const composeRouter = createTRPCRouter({ } } + if (domains && domains?.length > 0) { + for (const domain of domains) { + await createDomain({ + ...domain, + domainType: "compose", + certificateType: "none", + composeId: compose.composeId, + }); + } + } + return null; }), diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts index ec7f13d4f..309dd91ad 100644 --- a/apps/dokploy/server/api/routers/domain.ts +++ b/apps/dokploy/server/api/routers/domain.ts @@ -1,8 +1,12 @@ import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc"; import { apiCreateDomain, + apiCreateTraefikMeDomain, + apiFindCompose, apiFindDomain, apiFindDomainByApplication, + apiFindDomainByCompose, + apiFindOneApplication, apiUpdateDomain, } from "@/server/db/schema"; import { manageDomain, removeDomain } from "@/server/utils/traefik/domain"; @@ -12,8 +16,8 @@ import { createDomain, findDomainById, findDomainsByApplicationId, - generateDomain, - generateWildcard, + findDomainsByComposeId, + generateTraefikMeDomain, removeDomainById, updateDomainById, } from "../services/domain"; @@ -33,27 +37,30 @@ export const domainRouter = createTRPCRouter({ } }), byApplicationId: protectedProcedure - .input(apiFindDomainByApplication) + .input(apiFindOneApplication) .query(async ({ input }) => { return await findDomainsByApplicationId(input.applicationId); }), + byComposeId: protectedProcedure + .input(apiFindCompose) + .query(async ({ input }) => { + return await findDomainsByComposeId(input.composeId); + }), generateDomain: protectedProcedure - .input(apiFindDomainByApplication) + .input(apiCreateTraefikMeDomain) .mutation(async ({ input }) => { - return generateDomain(input); - }), - generateWildcard: protectedProcedure - .input(apiFindDomainByApplication) - .mutation(async ({ input }) => { - return generateWildcard(input); + return generateTraefikMeDomain(input.appName); }), + update: protectedProcedure .input(apiUpdateDomain) .mutation(async ({ input }) => { const result = await updateDomainById(input.domainId, input); const domain = await findDomainById(input.domainId); - const application = await findApplicationById(domain.applicationId); - await manageDomain(application, domain); + if (domain.applicationId) { + const application = await findApplicationById(domain.applicationId); + await manageDomain(application, domain); + } return result; }), one: protectedProcedure.input(apiFindDomain).query(async ({ input }) => { @@ -64,7 +71,9 @@ export const domainRouter = createTRPCRouter({ .mutation(async ({ input }) => { const domain = await findDomainById(input.domainId); const result = await removeDomainById(input.domainId); - await removeDomain(domain.application.appName, domain.uniqueConfigKey); + if (domain.application) { + await removeDomain(domain.application.appName, domain.uniqueConfigKey); + } return result; }), diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index d4dfb7ec5..5c3a4f01a 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -15,11 +15,13 @@ import { cleanUpSystemPrune, cleanUpUnusedImages, cleanUpUnusedVolumes, + prepareEnvironmentVariables, startService, stopService, } from "@/server/utils/docker/utils"; import { recreateDirectory } from "@/server/utils/filesystem/directory"; import { sendDockerCleanupNotifications } from "@/server/utils/notifications/docker-cleanup"; +import { execAsync } from "@/server/utils/process/execAsync"; import { spawnAsync } from "@/server/utils/process/spawnAsync"; import { readConfig, @@ -36,6 +38,7 @@ import { import { generateOpenApiDocument } from "@dokploy/trpc-openapi"; import { TRPCError } from "@trpc/server"; import { scheduleJob, scheduledJobs } from "node-schedule"; +import { z } from "zod"; import { appRouter } from "../root"; import { findAdmin, updateAdmin } from "../services/admin"; import { @@ -49,14 +52,10 @@ import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; export const settingsRouter = createTRPCRouter({ reloadServer: adminProcedure.mutation(async () => { - await spawnAsync("docker", [ - "service", - "update", - "--force", - "--image", - getDokployImage(), - "dokploy", - ]); + const { stdout } = await execAsync( + "docker service inspect dokploy --format '{{.ID}}'", + ); + await execAsync(`docker service update --force ${stdout.trim()}`); return true; }), reloadTraefik: adminProcedure.mutation(async () => { @@ -72,7 +71,9 @@ export const settingsRouter = createTRPCRouter({ toggleDashboard: adminProcedure .input(apiEnableDashboard) .mutation(async ({ input }) => { - await initializeTraefik(input.enableDashboard); + await initializeTraefik({ + enableDashboard: input.enableDashboard, + }); return true; }), @@ -312,4 +313,37 @@ export const settingsRouter = createTRPCRouter({ return openApiDocument; }, ), + readTraefikEnv: adminProcedure.query(async () => { + const { stdout } = await execAsync( + "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik", + ); + + return stdout.trim(); + }), + + writeTraefikEnv: adminProcedure + .input(z.string()) + .mutation(async ({ input }) => { + const envs = prepareEnvironmentVariables(input); + await initializeTraefik({ + env: envs, + }); + + return true; + }), + haveTraefikDashboardPortEnabled: adminProcedure.query(async () => { + const { stdout } = await execAsync( + "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik", + ); + + const parsed: any[] = JSON.parse(stdout.trim()); + + for (const port of parsed) { + if (port.PublishedPort === 8080) { + return true; + } + } + + return false; + }), }); diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index 1421e6175..eb2bf8af2 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -34,21 +34,23 @@ export const sshRouter = createTRPCRouter({ }); } }), - remove: adminProcedure.input(apiRemoveSshKey).mutation(async ({ input }) => { - try { - return await removeSSHKeyById(input.sshKeyId); - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error to delete this ssh key", - }); - } - }), + remove: protectedProcedure + .input(apiRemoveSshKey) + .mutation(async ({ input }) => { + try { + return await removeSSHKeyById(input.sshKeyId); + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to delete this ssh key", + }); + } + }), one: protectedProcedure.input(apiFindOneSshKey).query(async ({ input }) => { const sshKey = await findSSHKeyById(input.sshKeyId); return sshKey; }), - all: adminProcedure.query(async () => { + all: protectedProcedure.query(async () => { return await db.query.sshKeys.findMany({}); }), generate: protectedProcedure @@ -56,15 +58,17 @@ export const sshRouter = createTRPCRouter({ .mutation(async ({ input }) => { return await generateSSHKey(input.type); }), - update: adminProcedure.input(apiUpdateSshKey).mutation(async ({ input }) => { - try { - return await updateSSHKeyById(input); - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error to update this ssh key", - cause: error, - }); - } - }), + update: protectedProcedure + .input(apiUpdateSshKey) + .mutation(async ({ input }) => { + try { + return await updateSSHKeyById(input); + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to update this ssh key", + cause: error, + }); + } + }), }); diff --git a/apps/dokploy/server/api/services/compose.ts b/apps/dokploy/server/api/services/compose.ts index 978c8fb71..bd949bc38 100644 --- a/apps/dokploy/server/api/services/compose.ts +++ b/apps/dokploy/server/api/services/compose.ts @@ -4,6 +4,7 @@ import { db } from "@/server/db"; import { type apiCreateCompose, compose } from "@/server/db/schema"; import { generateAppName } from "@/server/db/schema/utils"; import { buildCompose } from "@/server/utils/builders/compose"; +import { cloneCompose, loadDockerCompose } from "@/server/utils/docker/domain"; import type { ComposeSpecification } from "@/server/utils/docker/types"; import { sendBuildErrorNotifications } from "@/server/utils/notifications/build-error"; import { sendBuildSuccessNotifications } from "@/server/utils/notifications/build-success"; @@ -14,7 +15,6 @@ import { createComposeFile } from "@/server/utils/providers/raw"; import { generatePassword } from "@/templates/utils"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -import { load } from "js-yaml"; import { findAdmin, getDokployUrl } from "./admin"; import { createDeploymentCompose, updateDeploymentStatus } from "./deployment"; import { validUniqueServerAppName } from "./project"; @@ -91,6 +91,7 @@ export const findComposeById = async (composeId: string) => { project: true, deployments: true, mounts: true, + domains: true, }, }); if (!result) { @@ -102,20 +103,27 @@ export const findComposeById = async (composeId: string) => { return result; }; -export const loadServices = async (composeId: string) => { +export const loadServices = async ( + composeId: string, + type: "fetch" | "cache" = "fetch", +) => { const compose = await findComposeById(composeId); - // use js-yaml to parse the docker compose file and then extact the services - const composeFile = compose.composeFile; - const composeData = load(composeFile) as ComposeSpecification; + if (type === "fetch") { + await cloneCompose(compose); + } + const composeData = await loadDockerCompose(compose); if (!composeData?.services) { - return ["All Services"]; + throw new TRPCError({ + code: "NOT_FOUND", + message: "Services not found", + }); } const services = Object.keys(composeData.services); - return [...services, "All Services"]; + return [...services]; }; export const updateCompose = async ( diff --git a/apps/dokploy/server/api/services/domain.ts b/apps/dokploy/server/api/services/domain.ts index b11c0d2ae..53687d28b 100644 --- a/apps/dokploy/server/api/services/domain.ts +++ b/apps/dokploy/server/api/services/domain.ts @@ -15,8 +15,6 @@ export type Domain = typeof domains.$inferSelect; export const createDomain = async (input: typeof apiCreateDomain._type) => { await db.transaction(async (tx) => { - const application = await findApplicationById(input.applicationId); - const domain = await tx .insert(domains) .values({ @@ -32,52 +30,19 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => { }); } - await manageDomain(application, domain); + if (domain.applicationId) { + const application = await findApplicationById(domain.applicationId); + await manageDomain(application, domain); + } }); }; -export const generateDomain = async ( - input: typeof apiFindDomainByApplication._type, -) => { - const application = await findApplicationById(input.applicationId); +export const generateTraefikMeDomain = async (appName: string) => { const admin = await findAdmin(); - const domain = await createDomain({ - applicationId: application.applicationId, - host: generateRandomDomain({ - serverIp: admin.serverIp || "", - projectName: application.appName, - }), - port: 3000, - certificateType: "none", - https: false, - path: "/", + return generateRandomDomain({ + serverIp: admin.serverIp || "", + projectName: appName, }); - - return domain; -}; - -export const generateWildcard = async ( - input: typeof apiFindDomainByApplication._type, -) => { - const application = await findApplicationById(input.applicationId); - const admin = await findAdmin(); - - if (!admin.host) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "We need a host to generate a wildcard domain", - }); - } - const domain = await createDomain({ - applicationId: application.applicationId, - host: generateWildcardDomain(application.appName, admin.host || ""), - port: 3000, - certificateType: "none", - https: false, - path: "/", - }); - - return domain; }; export const generateWildcardDomain = ( @@ -114,6 +79,17 @@ export const findDomainsByApplicationId = async (applicationId: string) => { return domainsArray; }; +export const findDomainsByComposeId = async (composeId: string) => { + const domainsArray = await db.query.domains.findMany({ + where: eq(domains.composeId, composeId), + with: { + compose: true, + }, + }); + + return domainsArray; +}; + export const updateDomainById = async ( domainId: string, domainData: Partial, diff --git a/apps/dokploy/server/db/schema/compose.ts b/apps/dokploy/server/db/schema/compose.ts index 485c51e10..80597d76f 100644 --- a/apps/dokploy/server/db/schema/compose.ts +++ b/apps/dokploy/server/db/schema/compose.ts @@ -1,11 +1,11 @@ import { sshKeys } from "@/server/db/schema/ssh-key"; -import { generatePassword } from "@/templates/utils"; import { relations } from "drizzle-orm"; import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { deployments } from "./deployment"; +import { domains } from "./domain"; import { mounts } from "./mount"; import { projects } from "./project"; import { applicationStatus } from "./shared"; @@ -72,6 +72,7 @@ export const composeRelations = relations(compose, ({ one, many }) => ({ fields: [compose.customGitSSHKeyId], references: [sshKeys.sshKeyId], }), + domains: many(domains), })); const createSchema = createInsertSchema(compose, { @@ -106,6 +107,11 @@ export const apiFindCompose = z.object({ composeId: z.string().min(1), }); +export const apiFetchServices = z.object({ + composeId: z.string().min(1), + type: z.enum(["fetch", "cache"]).optional().default("cache"), +}); + export const apiUpdateCompose = createSchema.partial().extend({ composeId: z.string(), composeFile: z.string().optional(), diff --git a/apps/dokploy/server/db/schema/domain.ts b/apps/dokploy/server/db/schema/domain.ts index 48dc05a77..301175fc5 100644 --- a/apps/dokploy/server/db/schema/domain.ts +++ b/apps/dokploy/server/db/schema/domain.ts @@ -1,11 +1,22 @@ -import { domain } from "@/server/db/validations"; +import { domain } from "@/server/db/validations/domain"; import { relations } from "drizzle-orm"; -import { boolean, integer, pgTable, serial, text } from "drizzle-orm/pg-core"; +import { + boolean, + integer, + pgEnum, + pgTable, + serial, + text, +} from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; +import { z } from "zod"; import { applications } from "./application"; +import { compose } from "./compose"; import { certificateType } from "./shared"; +export const domainType = pgEnum("domainType", ["compose", "application"]); + export const domains = pgTable("domain", { domainId: text("domainId") .notNull() @@ -13,15 +24,21 @@ export const domains = pgTable("domain", { .$defaultFn(() => nanoid()), host: text("host").notNull(), https: boolean("https").notNull().default(false), - port: integer("port").default(80), + port: integer("port").default(3000), path: text("path").default("/"), + serviceName: text("serviceName"), + domainType: domainType("domainType").default("application"), uniqueConfigKey: serial("uniqueConfigKey"), createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - applicationId: text("applicationId") - .notNull() - .references(() => applications.applicationId, { onDelete: "cascade" }), + composeId: text("composeId").references(() => compose.composeId, { + onDelete: "cascade", + }), + applicationId: text("applicationId").references( + () => applications.applicationId, + { onDelete: "cascade" }, + ), certificateType: certificateType("certificateType").notNull().default("none"), }); @@ -30,6 +47,10 @@ export const domainsRelations = relations(domains, ({ one }) => ({ fields: [domains.applicationId], references: [applications.applicationId], }), + compose: one(compose, { + fields: [domains.composeId], + references: [compose.composeId], + }), })); const createSchema = createInsertSchema(domains, domain._def.schema.shape); @@ -41,6 +62,9 @@ export const apiCreateDomain = createSchema.pick({ https: true, applicationId: true, certificateType: true, + composeId: true, + serviceName: true, + domainType: true, }); export const apiFindDomain = createSchema @@ -53,6 +77,14 @@ export const apiFindDomainByApplication = createSchema.pick({ applicationId: true, }); +export const apiCreateTraefikMeDomain = createSchema.pick({}).extend({ + appName: z.string().min(1), +}); + +export const apiFindDomainByCompose = createSchema.pick({ + composeId: true, +}); + export const apiUpdateDomain = createSchema .pick({ host: true, @@ -60,5 +92,7 @@ export const apiUpdateDomain = createSchema port: true, https: true, certificateType: true, + serviceName: true, + domainType: true, }) .merge(createSchema.pick({ domainId: true }).required()); diff --git a/apps/dokploy/server/db/schema/user.ts b/apps/dokploy/server/db/schema/user.ts index 50c1eb83d..2710be837 100644 --- a/apps/dokploy/server/db/schema/user.ts +++ b/apps/dokploy/server/db/schema/user.ts @@ -28,6 +28,7 @@ export const users = pgTable("user", { .notNull() .$defaultFn(() => new Date().toISOString()), canCreateProjects: boolean("canCreateProjects").notNull().default(false), + canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), canCreateServices: boolean("canCreateServices").notNull().default(false), canDeleteProjects: boolean("canDeleteProjects").notNull().default(false), canDeleteServices: boolean("canDeleteServices").notNull().default(false), @@ -107,6 +108,7 @@ export const apiAssignPermissions = createSchema canAccessToTraefikFiles: true, canAccessToDocker: true, canAccessToAPI: true, + canAccessToSSHKeys: true, }) .required(); diff --git a/apps/dokploy/server/db/validations/domain.ts b/apps/dokploy/server/db/validations/domain.ts new file mode 100644 index 000000000..b09b57b6c --- /dev/null +++ b/apps/dokploy/server/db/validations/domain.ts @@ -0,0 +1,46 @@ +import { z } from "zod"; + +export const domain = z + .object({ + host: z.string().min(1, { message: "Add a hostname" }), + path: z.string().min(1).optional(), + port: z + .number() + .min(1, { message: "Port must be at least 1" }) + .max(65535, { message: "Port must be 65535 or below" }) + .optional(), + https: z.boolean().optional(), + certificateType: z.enum(["letsencrypt", "none"]).optional(), + }) + .superRefine((input, ctx) => { + if (input.https && !input.certificateType) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["certificateType"], + message: "Required", + }); + } + }); + +export const domainCompose = z + .object({ + host: z.string().min(1, { message: "Host is required" }), + path: z.string().min(1).optional(), + port: z + .number() + .min(1, { message: "Port must be at least 1" }) + .max(65535, { message: "Port must be 65535 or below" }) + .optional(), + https: z.boolean().optional(), + certificateType: z.enum(["letsencrypt", "none"]).optional(), + serviceName: z.string().min(1, { message: "Service name is required" }), + }) + .superRefine((input, ctx) => { + if (input.https && !input.certificateType) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["certificateType"], + message: "Required", + }); + } + }); diff --git a/apps/dokploy/server/db/validations/index.ts b/apps/dokploy/server/db/validations/index.ts index cc97ee933..b3cb57852 100644 --- a/apps/dokploy/server/db/validations/index.ts +++ b/apps/dokploy/server/db/validations/index.ts @@ -35,27 +35,3 @@ export const sshKeyUpdate = sshKeyCreate.pick({ export const sshKeyType = z.object({ type: z.enum(["rsa", "ed25519"]).optional(), }); - -export const domain = z - .object({ - host: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9\.-]*\.[a-zA-Z]{2,}$/, { - message: "Invalid hostname", - }), - path: z.string().min(1).optional(), - port: z - .number() - .min(1, { message: "Port must be at least 1" }) - .max(65535, { message: "Port must be 65535 or below" }) - .optional(), - https: z.boolean().optional(), - certificateType: z.enum(["letsencrypt", "none"]).optional(), - }) - .superRefine((input, ctx) => { - if (input.https && !input.certificateType) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - path: ["certificateType"], - message: "Required", - }); - } - }); diff --git a/apps/dokploy/server/setup/postgres-setup.ts b/apps/dokploy/server/setup/postgres-setup.ts index 139a2f35d..ca606c2d1 100644 --- a/apps/dokploy/server/setup/postgres-setup.ts +++ b/apps/dokploy/server/setup/postgres-setup.ts @@ -36,10 +36,9 @@ export const initializePostgres = async () => { Ports: [ { TargetPort: 5432, - ...(process.env.NODE_ENV === "development" - ? { PublishedPort: 5432 } - : {}), + PublishedPort: process.env.NODE_ENV === "development" ? 5432 : 0, Protocol: "tcp", + PublishMode: "host", }, ], }, diff --git a/apps/dokploy/server/setup/redis-setup.ts b/apps/dokploy/server/setup/redis-setup.ts index 0f9719a98..abbacdd83 100644 --- a/apps/dokploy/server/setup/redis-setup.ts +++ b/apps/dokploy/server/setup/redis-setup.ts @@ -33,10 +33,9 @@ export const initializeRedis = async () => { Ports: [ { TargetPort: 6379, - ...(process.env.NODE_ENV === "development" - ? { PublishedPort: 6379 } - : {}), + PublishedPort: process.env.NODE_ENV === "development" ? 6379 : 0, Protocol: "tcp", + PublishMode: "host", }, ], }, diff --git a/apps/dokploy/server/setup/traefik-setup.ts b/apps/dokploy/server/setup/traefik-setup.ts index 2b44cf401..afebf07e0 100644 --- a/apps/dokploy/server/setup/traefik-setup.ts +++ b/apps/dokploy/server/setup/traefik-setup.ts @@ -11,7 +11,15 @@ const TRAEFIK_SSL_PORT = Number.parseInt(process.env.TRAEFIK_SSL_PORT ?? "", 10) || 443; const TRAEFIK_PORT = Number.parseInt(process.env.TRAEFIK_PORT ?? "", 10) || 80; -export const initializeTraefik = async (enableDashboard = false) => { +interface TraefikOptions { + enableDashboard?: boolean; + env?: string[]; +} + +export const initializeTraefik = async ({ + enableDashboard = false, + env = [], +}: TraefikOptions = {}) => { const imageName = "traefik:v2.5"; const containerName = "dokploy-traefik"; const settings: CreateServiceOptions = { @@ -19,6 +27,7 @@ export const initializeTraefik = async (enableDashboard = false) => { TaskTemplate: { ContainerSpec: { Image: imageName, + Env: env, Mounts: [ { Type: "bind", diff --git a/apps/dokploy/server/utils/builders/compose.ts b/apps/dokploy/server/utils/builders/compose.ts index 9212cf604..4f72f46d3 100644 --- a/apps/dokploy/server/utils/builders/compose.ts +++ b/apps/dokploy/server/utils/builders/compose.ts @@ -8,20 +8,28 @@ import { dirname, join } from "node:path"; import { COMPOSE_PATH } from "@/server/constants"; import type { InferResultType } from "@/server/types/with"; import boxen from "boxen"; +import { writeDomainsToCompose } from "../docker/domain"; import { prepareEnvironmentVariables } from "../docker/utils"; import { spawnAsync } from "../process/spawnAsync"; export type ComposeNested = InferResultType< "compose", - { project: true; mounts: true } + { project: true; mounts: true; domains: true } >; export const buildCompose = async (compose: ComposeNested, logPath: string) => { const writeStream = createWriteStream(logPath, { flags: "a" }); - const { sourceType, appName, mounts, composeType, env, composePath } = - compose; + const { + sourceType, + appName, + mounts, + composeType, + env, + composePath, + domains, + } = compose; try { const command = createCommand(compose); - + await writeDomainsToCompose(compose, domains); createEnvFile(compose); const logContent = ` diff --git a/apps/dokploy/server/utils/docker/domain.ts b/apps/dokploy/server/utils/docker/domain.ts new file mode 100644 index 000000000..ab4a904bd --- /dev/null +++ b/apps/dokploy/server/utils/docker/domain.ts @@ -0,0 +1,220 @@ +import fs, { existsSync, readFileSync, writeSync } from "node:fs"; +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import type { Compose } from "@/server/api/services/compose"; +import type { Domain } from "@/server/api/services/domain"; +import { COMPOSE_PATH } from "@/server/constants"; +import { dump, load } from "js-yaml"; +import { cloneGitRawRepository } from "../providers/git"; +import { cloneRawGithubRepository } from "../providers/github"; +import { createComposeFileRaw } from "../providers/raw"; +import type { + ComposeSpecification, + DefinitionsService, + PropertiesNetworks, +} from "./types"; + +export const cloneCompose = async (compose: Compose) => { + if (compose.sourceType === "github") { + await cloneRawGithubRepository(compose); + } else if (compose.sourceType === "git") { + await cloneGitRawRepository(compose); + } else if (compose.sourceType === "raw") { + await createComposeFileRaw(compose); + } +}; + +export const getComposePath = (compose: Compose) => { + const { appName, sourceType, composePath } = compose; + let path = ""; + + if (sourceType === "raw") { + path = "docker-compose.yml"; + } else { + path = composePath; + } + + return join(COMPOSE_PATH, appName, "code", path); +}; + +export const loadDockerCompose = async ( + compose: Compose, +): Promise => { + const path = getComposePath(compose); + + if (existsSync(path)) { + const yamlStr = readFileSync(path, "utf8"); + const parsedConfig = load(yamlStr) as ComposeSpecification; + return parsedConfig; + } + return null; +}; + +export const readComposeFile = async (compose: Compose) => { + const path = getComposePath(compose); + if (existsSync(path)) { + const yamlStr = readFileSync(path, "utf8"); + return yamlStr; + } + return null; +}; + +export const writeDomainsToCompose = async ( + compose: Compose, + domains: Domain[], +) => { + if (!domains.length) { + return; + } + const composeConverted = await addDomainToCompose(compose, domains); + + const path = getComposePath(compose); + const composeString = dump(composeConverted, { lineWidth: 1000 }); + try { + await writeFile(path, composeString, "utf8"); + } catch (error) { + throw error; + } +}; + +export const addDomainToCompose = async ( + compose: Compose, + domains: Domain[], +) => { + const { appName } = compose; + const result = await loadDockerCompose(compose); + + if (!result || domains.length === 0) { + return null; + } + + for (const domain of domains) { + const { serviceName, https } = domain; + if (!serviceName) { + throw new Error("Service name not found"); + } + if (!result?.services?.[serviceName]) { + throw new Error(`The service ${serviceName} not found in the compose`); + } + if (!result.services[serviceName].labels) { + result.services[serviceName].labels = []; + } + + const httpLabels = await createDomainLabels(appName, domain, "web"); + if (https) { + const httpsLabels = await createDomainLabels( + appName, + domain, + "websecure", + ); + httpLabels.push(...httpsLabels); + } + + const labels = result.services[serviceName].labels; + + if (Array.isArray(labels)) { + if (!labels.includes("traefik.enable=true")) { + labels.push("traefik.enable=true"); + } + labels.push(...httpLabels); + } + + // Add the dokploy-network to the service + result.services[serviceName].networks = addDokployNetworkToService( + result.services[serviceName].networks, + ); + } + + // Add dokploy-network to the root of the compose file + result.networks = addDokployNetworkToRoot(result.networks); + + return result; +}; + +export const writeComposeFile = async ( + compose: Compose, + composeSpec: ComposeSpecification, +) => { + const path = getComposePath(compose); + + try { + const composeFile = dump(composeSpec, { + lineWidth: 1000, + }); + fs.writeFileSync(path, composeFile, "utf8"); + } catch (e) { + console.error("Error saving the YAML config file:", e); + } +}; + +export const createDomainLabels = async ( + appName: string, + domain: Domain, + entrypoint: "web" | "websecure", +) => { + const { host, port, https, uniqueConfigKey, certificateType } = domain; + const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`; + const labels = [ + `traefik.http.routers.${routerName}.rule=Host(\`${host}\`)`, + `traefik.http.routers.${routerName}.entrypoints=${entrypoint}`, + `traefik.http.services.${routerName}.loadbalancer.server.port=${port}`, + `traefik.http.routers.${routerName}.service=${routerName}`, + ]; + + if (entrypoint === "web" && https) { + labels.push( + `traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`, + ); + } + + if (entrypoint === "websecure") { + if (certificateType === "letsencrypt") { + labels.push( + `traefik.http.routers.${routerName}.tls.certresolver=letsencrypt`, + ); + } + } + + return labels; +}; + +export const addDokployNetworkToService = ( + networkService: DefinitionsService["networks"], +) => { + let networks = networkService; + const network = "dokploy-network"; + if (!networks) { + networks = []; + } + + if (Array.isArray(networks)) { + if (!networks.includes(network)) { + networks.push(network); + } + } else if (networks && typeof networks === "object") { + if (!(network in networks)) { + networks[network] = {}; + } + } + + return networks; +}; + +export const addDokployNetworkToRoot = ( + networkRoot: PropertiesNetworks | undefined, +) => { + let networks = networkRoot; + const network = "dokploy-network"; + + if (!networks) { + networks = {}; + } + + if (networks[network] || !networks[network]) { + networks[network] = { + external: true, + }; + } + + return networks; +}; diff --git a/apps/dokploy/server/utils/providers/git.ts b/apps/dokploy/server/utils/providers/git.ts index cf3e28aea..bad7e615f 100644 --- a/apps/dokploy/server/utils/providers/git.ts +++ b/apps/dokploy/server/utils/providers/git.ts @@ -139,3 +139,61 @@ const sanitizeRepoPathSSH = (input: string) => { }, }; }; + +export const cloneGitRawRepository = async (entity: { + appName: string; + customGitUrl?: string | null; + customGitBranch?: string | null; + customGitSSHKeyId?: string | null; +}) => { + const { appName, customGitUrl, customGitBranch, customGitSSHKeyId } = entity; + + if (!customGitUrl || !customGitBranch) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error: Repository not found", + }); + } + + const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`); + const basePath = COMPOSE_PATH; + const outputPath = join(basePath, appName, "code"); + const knownHostsPath = path.join(SSH_PATH, "known_hosts"); + + try { + await addHostToKnownHosts(customGitUrl); + await recreateDirectory(outputPath); + + if (customGitSSHKeyId) { + await updateSSHKeyById({ + sshKeyId: customGitSSHKeyId, + lastUsedAt: new Date().toISOString(), + }); + } + + await spawnAsync( + "git", + [ + "clone", + "--branch", + customGitBranch, + "--depth", + "1", + customGitUrl, + outputPath, + "--progress", + ], + (data) => {}, + { + env: { + ...process.env, + ...(customGitSSHKeyId && { + GIT_SSH_COMMAND: `ssh -i ${keyPath} -o UserKnownHostsFile=${knownHostsPath}`, + }), + }, + }, + ); + } catch (error) { + throw error; + } +}; diff --git a/apps/dokploy/server/utils/providers/github.ts b/apps/dokploy/server/utils/providers/github.ts index 06e38e191..a49f2b56e 100644 --- a/apps/dokploy/server/utils/providers/github.ts +++ b/apps/dokploy/server/utils/providers/github.ts @@ -1,6 +1,6 @@ import { createWriteStream } from "node:fs"; import { join } from "node:path"; -import type { Admin } from "@/server/api/services/admin"; +import { type Admin, findAdmin } from "@/server/api/services/admin"; import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants"; import { createAppAuth } from "@octokit/auth-app"; import { TRPCError } from "@trpc/server"; @@ -128,3 +128,34 @@ export const cloneGithubRepository = async ( writeStream.end(); } }; + +export const cloneRawGithubRepository = async (entity: { + appName: string; + repository?: string | null; + owner?: string | null; + branch?: string | null; +}) => { + const { appName, repository, owner, branch } = entity; + const admin = await findAdmin(); + const basePath = COMPOSE_PATH; + const outputPath = join(basePath, appName, "code"); + const octokit = authGithub(admin); + const token = await getGithubToken(octokit); + const repoclone = `github.com/${owner}/${repository}.git`; + await recreateDirectory(outputPath); + const cloneUrl = `https://oauth2:${token}@${repoclone}`; + try { + await spawnAsync("git", [ + "clone", + "--branch", + branch!, + "--depth", + "1", + cloneUrl, + outputPath, + "--progress", + ]); + } catch (error) { + throw error; + } +}; diff --git a/apps/dokploy/server/utils/providers/raw.ts b/apps/dokploy/server/utils/providers/raw.ts index 63b825a7e..9bf818a30 100644 --- a/apps/dokploy/server/utils/providers/raw.ts +++ b/apps/dokploy/server/utils/providers/raw.ts @@ -26,3 +26,15 @@ export const createComposeFile = async (compose: Compose, logPath: string) => { writeStream.end(); } }; + +export const createComposeFileRaw = async (compose: Compose) => { + const { appName, composeFile } = compose; + const outputPath = join(COMPOSE_PATH, appName, "code"); + const filePath = join(outputPath, "docker-compose.yml"); + try { + await recreateDirectory(outputPath); + await writeFile(filePath, composeFile); + } catch (error) { + throw error; + } +}; diff --git a/apps/dokploy/templates/appsmith/docker-compose.yml b/apps/dokploy/templates/appsmith/docker-compose.yml index ad07a709b..f520ee362 100644 --- a/apps/dokploy/templates/appsmith/docker-compose.yml +++ b/apps/dokploy/templates/appsmith/docker-compose.yml @@ -2,17 +2,5 @@ version: "3.8" services: appsmith: image: index.docker.io/appsmith/appsmith-ee:v1.29 - networks: - - dokploy-network - ports: - - ${APP_SMITH_PORT} - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${APP_SMITH_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${APP_SMITH_PORT}" volumes: - ../files/stacks:/appsmith-stacks - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/appsmith/index.ts b/apps/dokploy/templates/appsmith/index.ts index 8da281323..ff744a249 100644 --- a/apps/dokploy/templates/appsmith/index.ts +++ b/apps/dokploy/templates/appsmith/index.ts @@ -1,4 +1,5 @@ import { + type DomainSchema, type Schema, type Template, generateHash, @@ -7,14 +8,16 @@ import { export function generate(schema: Schema): Template { const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - const envs = [ - `APP_SMITH_HOST=${randomDomain}`, - "APP_SMITH_PORT=80", - `HASH=${mainServiceHash}`, + + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 80, + serviceName: "appsmith", + }, ]; return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/aptabase/docker-compose.yml b/apps/dokploy/templates/aptabase/docker-compose.yml new file mode 100644 index 000000000..934fd1eea --- /dev/null +++ b/apps/dokploy/templates/aptabase/docker-compose.yml @@ -0,0 +1,51 @@ +services: + aptabase_db: + image: postgres:15-alpine + restart: always + volumes: + - db-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: aptabase + POSTGRES_PASSWORD: sTr0NGp4ssw0rd + networks: + - dokploy-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U aptabase"] + interval: 10s + timeout: 5s + retries: 5 + + aptabase_events_db: + image: clickhouse/clickhouse-server:23.8.16.16-alpine + restart: always + volumes: + - events-db-data:/var/lib/clickhouse + environment: + CLICKHOUSE_USER: aptabase + CLICKHOUSE_PASSWORD: sTr0NGp4ssw0rd + ulimits: + nofile: + soft: 262144 + hard: 262144 + networks: + - dokploy-network + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8123 || exit 1"] + interval: 10s + timeout: 5s + retries: 5 + + aptabase: + image: ghcr.io/aptabase/aptabase:main + restart: always + environment: + BASE_URL: http://${APTABASE_HOST} + AUTH_SECRET: ${AUTH_SECRET} + DATABASE_URL: Server=aptabase_db;Port=5432;User Id=aptabase;Password=sTr0NGp4ssw0rd;Database=aptabase + CLICKHOUSE_URL: Host=aptabase_events_db;Port=8123;Username=aptabase;Password=sTr0NGp4ssw0rd + +volumes: + db-data: + driver: local + events-db-data: + driver: local diff --git a/apps/dokploy/templates/aptabase/index.ts b/apps/dokploy/templates/aptabase/index.ts new file mode 100644 index 000000000..38b077ae8 --- /dev/null +++ b/apps/dokploy/templates/aptabase/index.ts @@ -0,0 +1,27 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const authSecret = generateBase64(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 8080, + serviceName: "aptabase", + }, + ]; + + const envs = [`APTABASE_HOST=${mainDomain}`, `AUTH_SECRET=${authSecret}`]; + + return { + envs, + domains, + }; +} diff --git a/apps/dokploy/templates/baserow/docker-compose.yml b/apps/dokploy/templates/baserow/docker-compose.yml index ffb3e8ad8..db588e83a 100644 --- a/apps/dokploy/templates/baserow/docker-compose.yml +++ b/apps/dokploy/templates/baserow/docker-compose.yml @@ -2,21 +2,9 @@ version: "3.8" services: baserow: image: baserow/baserow:1.25.2 - networks: - - dokploy-network environment: BASEROW_PUBLIC_URL: "http://${BASEROW_HOST}" - ports: - - ${BASEROW_PORT} - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${BASEROW_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${BASEROW_PORT} volumes: - baserow_data:/baserow/data volumes: baserow_data: - -networks: - dokploy-network: - external: true \ No newline at end of file diff --git a/apps/dokploy/templates/baserow/index.ts b/apps/dokploy/templates/baserow/index.ts index a0a387198..fa57417cc 100644 --- a/apps/dokploy/templates/baserow/index.ts +++ b/apps/dokploy/templates/baserow/index.ts @@ -1,20 +1,24 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - const envs = [ - `BASEROW_HOST=${randomDomain}`, - "BASEROW_PORT=80", - `HASH=${mainServiceHash}`, + const mainHost = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainHost, + port: 80, + serviceName: "baserow", + }, ]; + const envs = [`BASEROW_HOST=${mainHost}`]; return { envs, + domains, }; } diff --git a/apps/dokploy/templates/calcom/docker-compose.yml b/apps/dokploy/templates/calcom/docker-compose.yml index 1afb2db87..7a1d8c92a 100644 --- a/apps/dokploy/templates/calcom/docker-compose.yml +++ b/apps/dokploy/templates/calcom/docker-compose.yml @@ -21,16 +21,6 @@ services: - DATABASE_URL=postgres://postgres:password@postgres:5432/db - NEXT_PUBLIC_WEBAPP_URL=http://${CALCOM_HOST} - NEXTAUTH_URL=http://${CALCOM_HOST}/api/auth - networks: - - dokploy-network - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${CALCOM_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${CALCOM_PORT}" - -networks: - dokploy-network: - external: true volumes: calcom-data: diff --git a/apps/dokploy/templates/calcom/index.ts b/apps/dokploy/templates/calcom/index.ts index 61c590bf1..d359e9c7c 100644 --- a/apps/dokploy/templates/calcom/index.ts +++ b/apps/dokploy/templates/calcom/index.ts @@ -1,27 +1,32 @@ import { + type DomainSchema, type Schema, type Template, generateBase64, - generateHash, generateRandomDomain, } from "../utils"; -// https://cal.com/ export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const calcomEncryptionKey = generateBase64(32); const nextAuthSecret = generateBase64(32); + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "calcom", + }, + ]; + const envs = [ - `CALCOM_HOST=${randomDomain}`, - "CALCOM_PORT=3000", - `HASH=${mainServiceHash}`, + `CALCOM_HOST=${mainDomain}`, `NEXTAUTH_SECRET=${nextAuthSecret}`, `CALENDSO_ENCRYPTION_KEY=${calcomEncryptionKey}`, ]; return { envs, + domains, }; } diff --git a/apps/dokploy/templates/directus/docker-compose.yml b/apps/dokploy/templates/directus/docker-compose.yml index 08a5db45f..6f058ba61 100644 --- a/apps/dokploy/templates/directus/docker-compose.yml +++ b/apps/dokploy/templates/directus/docker-compose.yml @@ -18,8 +18,6 @@ services: directus: image: directus/directus:10.12.1 - networks: - - dokploy-network ports: - 8055 volumes: @@ -28,10 +26,6 @@ services: depends_on: - cache - database - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${DIRECTUS_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${DIRECTUS_PORT} environment: SECRET: "replace-with-secure-random-value" @@ -49,8 +43,5 @@ services: ADMIN_EMAIL: "admin@example.com" ADMIN_PASSWORD: "d1r3ctu5" -networks: - dokploy-network: - external: true volumes: directus: diff --git a/apps/dokploy/templates/directus/index.ts b/apps/dokploy/templates/directus/index.ts index 94bedbb1c..42a05aee0 100644 --- a/apps/dokploy/templates/directus/index.ts +++ b/apps/dokploy/templates/directus/index.ts @@ -1,20 +1,20 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - const envs = [ - `DIRECTUS_HOST=${randomDomain}`, - "DIRECTUS_PORT=8055", - `HASH=${mainServiceHash}`, + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 8055, + serviceName: "directus", + }, ]; return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/documenso/docker-compose.yml b/apps/dokploy/templates/documenso/docker-compose.yml index f411cfbb4..a649fa757 100644 --- a/apps/dokploy/templates/documenso/docker-compose.yml +++ b/apps/dokploy/templates/documenso/docker-compose.yml @@ -19,8 +19,6 @@ services: documenso: image: documenso/documenso:1.5.6-rc.2 - networks: - - dokploy-network depends_on: postgres: condition: service_healthy @@ -38,16 +36,8 @@ services: - NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=/opt/documenso/cert.p12 ports: - ${DOCUMENSO_PORT} - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${DOCUMENSO_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${DOCUMENSO_PORT}" volumes: - /opt/documenso/cert.p12:/opt/documenso/cert.p12 -networks: - dokploy-network: - external: true - volumes: documenso-data: diff --git a/apps/dokploy/templates/documenso/index.ts b/apps/dokploy/templates/documenso/index.ts index 67afd596d..c70d1db4a 100644 --- a/apps/dokploy/templates/documenso/index.ts +++ b/apps/dokploy/templates/documenso/index.ts @@ -1,24 +1,29 @@ import { + type DomainSchema, type Schema, type Template, generateBase64, - generateHash, generatePassword, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - + const mainDomain = generateRandomDomain(schema); const nextAuthSecret = generateBase64(32); const documensoEncryptionKey = generatePassword(32); const documensoSecondaryEncryptionKey = generatePassword(64); + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "documenso", + }, + ]; + const envs = [ - `DOCUMENSO_HOST=${randomDomain}`, + `DOCUMENSO_HOST=${mainDomain}`, "DOCUMENSO_PORT=3000", - `HASH=${mainServiceHash}`, `NEXTAUTH_SECRET=${nextAuthSecret}`, `NEXT_PRIVATE_ENCRYPTION_KEY=${documensoEncryptionKey}`, `NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=${documensoSecondaryEncryptionKey}`, @@ -26,5 +31,6 @@ export function generate(schema: Schema): Template { return { envs, + domains, }; } diff --git a/apps/dokploy/templates/doublezero/docker-compose.yml b/apps/dokploy/templates/doublezero/docker-compose.yml index bb7b1d2b3..352470334 100644 --- a/apps/dokploy/templates/doublezero/docker-compose.yml +++ b/apps/dokploy/templates/doublezero/docker-compose.yml @@ -2,10 +2,6 @@ services: doublezero: restart: always image: liltechnomancer/double-zero:0.2.1 - ports: - - ${DOUBLEZERO_PORT} - networks: - - dokploy-network volumes: - db-data:/var/lib/doublezero/data environment: @@ -17,15 +13,7 @@ services: SECRET_KEY_BASE: ${SECRET_KEY_BASE} PHX_HOST: ${DOUBLEZERO_HOST} DATABASE_PATH: ./00.db - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${DOUBLEZERO_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${DOUBLEZERO_PORT}" - + volumes: db-data: driver: local - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/doublezero/index.ts b/apps/dokploy/templates/doublezero/index.ts index 52656f401..fa774e9dc 100644 --- a/apps/dokploy/templates/doublezero/index.ts +++ b/apps/dokploy/templates/doublezero/index.ts @@ -1,20 +1,26 @@ import { + type DomainSchema, type Schema, type Template, generateBase64, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const secretKeyBase = generateBase64(64); + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 4000, + serviceName: "doublezero", + }, + ]; + const envs = [ - `DOUBLEZERO_HOST=${randomDomain}`, + `DOUBLEZERO_HOST=${mainDomain}`, "DOUBLEZERO_PORT=4000", - `HASH=${mainServiceHash}`, `SECRET_KEY_BASE=${secretKeyBase}`, "AWS_ACCESS_KEY_ID=your-aws-access-key", "AWS_SECRET_ACCESS_KEY=your-aws-secret-key", @@ -25,5 +31,6 @@ export function generate(schema: Schema): Template { return { envs, + domains, }; } diff --git a/apps/dokploy/templates/excalidraw/docker-compose.yml b/apps/dokploy/templates/excalidraw/docker-compose.yml index 58920b393..8743434b6 100644 --- a/apps/dokploy/templates/excalidraw/docker-compose.yml +++ b/apps/dokploy/templates/excalidraw/docker-compose.yml @@ -1,17 +1,7 @@ -version: '3.8' +version: "3.8" services: excalidraw: networks: - dokploy-network image: excalidraw/excalidraw:latest - ports: - - ${EXCALIDRAW_PORT} - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${EXCALIDRAW_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${EXCALIDRAW_PORT} - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/excalidraw/index.ts b/apps/dokploy/templates/excalidraw/index.ts index 10e4bbcff..13a43c440 100644 --- a/apps/dokploy/templates/excalidraw/index.ts +++ b/apps/dokploy/templates/excalidraw/index.ts @@ -1,4 +1,5 @@ import { + type DomainSchema, type Schema, type Template, generateHash, @@ -6,15 +7,17 @@ import { } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - const envs = [ - `EXCALIDRAW_HOST=${randomDomain}`, - "EXCALIDRAW_PORT=80", - `HASH=${mainServiceHash}`, + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 80, + serviceName: "excalidraw", + }, ]; return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/ghost/docker-compose.yml b/apps/dokploy/templates/ghost/docker-compose.yml index a507b887e..288c59e54 100644 --- a/apps/dokploy/templates/ghost/docker-compose.yml +++ b/apps/dokploy/templates/ghost/docker-compose.yml @@ -1,13 +1,8 @@ version: "3.8" services: - ghost: image: ghost:5-alpine restart: always - networks: - - dokploy-network - ports: - - ${GHOST_PORT} environment: database__client: mysql database__connection__host: db @@ -15,10 +10,7 @@ services: database__connection__password: example database__connection__database: ghost url: http://${GHOST_HOST} - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${GHOST_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${GHOST_PORT} + volumes: - ghost:/var/lib/ghost/content @@ -35,7 +27,3 @@ services: volumes: ghost: db: - -networks: - dokploy-network: - external: true \ No newline at end of file diff --git a/apps/dokploy/templates/ghost/index.ts b/apps/dokploy/templates/ghost/index.ts index 178126af6..1a88c3629 100644 --- a/apps/dokploy/templates/ghost/index.ts +++ b/apps/dokploy/templates/ghost/index.ts @@ -1,4 +1,5 @@ import { + type DomainSchema, type Schema, type Template, generateHash, @@ -6,15 +7,19 @@ import { } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - const envs = [ - `GHOST_HOST=${randomDomain}`, - "GHOST_PORT=2368", - `HASH=${mainServiceHash}`, + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 2368, + serviceName: "ghost", + }, ]; + const envs = [`GHOST_HOST=${mainDomain}`]; return { envs, + domains, }; } diff --git a/apps/dokploy/templates/glitchtip/docker-compose.yml b/apps/dokploy/templates/glitchtip/docker-compose.yml index a654a91b2..e45c76627 100644 --- a/apps/dokploy/templates/glitchtip/docker-compose.yml +++ b/apps/dokploy/templates/glitchtip/docker-compose.yml @@ -1,16 +1,14 @@ -x-environment: - &default-environment +x-environment: &default-environment DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres SECRET_KEY: ${SECRET_KEY} PORT: ${GLITCHTIP_PORT} EMAIL_URL: consolemail:// - GLITCHTIP_DOMAIN: http://${GLITCHTIP_HOST} + GLITCHTIP_DOMAIN: http://${GLITCHTIP_HOST} DEFAULT_FROM_EMAIL: email@glitchtip.com - CELERY_WORKER_AUTOSCALE: "1,3" + CELERY_WORKER_AUTOSCALE: "1,3" CELERY_WORKER_MAX_TASKS_PER_CHILD: "10000" -x-depends_on: - &default-depends_on +x-depends_on: &default-depends_on - postgres - redis @@ -18,7 +16,7 @@ services: postgres: image: postgres:16 environment: - POSTGRES_HOST_AUTH_METHOD: "trust" + POSTGRES_HOST_AUTH_METHOD: "trust" restart: unless-stopped volumes: - pg-data:/var/lib/postgresql/data @@ -36,21 +34,15 @@ services: - ${GLITCHTIP_PORT} environment: *default-environment restart: unless-stopped - volumes: + volumes: - uploads:/code/uploads - networks: - - dokploy-network - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${GLITCHTIP_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${GLITCHTIP_PORT} worker: image: glitchtip/glitchtip:v4.0 command: ./bin/run-celery-with-beat.sh depends_on: *default-depends_on environment: *default-environment restart: unless-stopped - volumes: + volumes: - uploads:/code/uploads networks: - dokploy-network @@ -65,7 +57,3 @@ services: volumes: pg-data: uploads: - -networks: - dokploy-network: - external: true \ No newline at end of file diff --git a/apps/dokploy/templates/glitchtip/index.ts b/apps/dokploy/templates/glitchtip/index.ts index 024d4106c..093d752e1 100644 --- a/apps/dokploy/templates/glitchtip/index.ts +++ b/apps/dokploy/templates/glitchtip/index.ts @@ -1,23 +1,30 @@ import { + type DomainSchema, type Schema, type Template, generateBase64, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const secretKey = generateBase64(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 8000, + serviceName: "web", + }, + ]; const envs = [ - `GLITCHTIP_HOST=${randomDomain}`, + `GLITCHTIP_HOST=${mainDomain}`, "GLITCHTIP_PORT=8000", `SECRET_KEY=${secretKey}`, - `HASH=${mainServiceHash}`, ]; return { envs, + domains, }; } diff --git a/apps/dokploy/templates/grafana/docker-compose.yml b/apps/dokploy/templates/grafana/docker-compose.yml index a0555f9fe..9d913c17f 100644 --- a/apps/dokploy/templates/grafana/docker-compose.yml +++ b/apps/dokploy/templates/grafana/docker-compose.yml @@ -1,20 +1,9 @@ version: "3.8" services: grafana: - networks: - - dokploy-network image: grafana/grafana-enterprise:9.5.20 restart: unless-stopped - ports: - - ${GRAFANA_PORT} - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${GRAFANA_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${GRAFANA_PORT} volumes: - grafana-storage:/var/lib/grafana -networks: - dokploy-network: - external: true volumes: - grafana-storage: {} \ No newline at end of file + grafana-storage: {} diff --git a/apps/dokploy/templates/grafana/index.ts b/apps/dokploy/templates/grafana/index.ts index fc66f56dd..fb614ef32 100644 --- a/apps/dokploy/templates/grafana/index.ts +++ b/apps/dokploy/templates/grafana/index.ts @@ -1,20 +1,19 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - const envs = [ - `GRAFANA_HOST=${randomDomain}`, - "GRAFANA_PORT=3000", - `HASH=${mainServiceHash}`, + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 3000, + serviceName: "grafana", + }, ]; - return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/jellyfin/docker-compose.yml b/apps/dokploy/templates/jellyfin/docker-compose.yml index 16ae6be96..cb61476ab 100644 --- a/apps/dokploy/templates/jellyfin/docker-compose.yml +++ b/apps/dokploy/templates/jellyfin/docker-compose.yml @@ -1,30 +1,19 @@ -version: '3.8' +version: "3.8" services: jellyfin: image: jellyfin/jellyfin:10 - networks: - - dokploy-network - ports: - - ${JELLYFIN_PORT} - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${JELLYFIN_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${JELLYFIN_PORT}" volumes: - config:/config - cache:/cache - media:/media - restart: 'unless-stopped' + restart: "unless-stopped" # Optional - alternative address used for autodiscovery environment: - JELLYFIN_PublishedServerUrl=http://${JELLYFIN_HOST} # Optional - may be necessary for docker healthcheck to pass if running in host network mode extra_hosts: - - 'host.docker.internal:host-gateway' + - "host.docker.internal:host-gateway" volumes: config: cache: media: -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/jellyfin/index.ts b/apps/dokploy/templates/jellyfin/index.ts index dc33b121a..61c9c9b7c 100644 --- a/apps/dokploy/templates/jellyfin/index.ts +++ b/apps/dokploy/templates/jellyfin/index.ts @@ -1,22 +1,25 @@ // EXAMPLE import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); - const port = 8096; - const envs = [ - `JELLYFIN_HOST=${randomDomain}`, - `HASH=${mainServiceHash}`, - `JELLYFIN_PORT=${port}`, + const domain = generateRandomDomain(schema); + const domains: DomainSchema[] = [ + { + host: domain, + port: 8096, + serviceName: "jellyfin", + }, ]; + const envs = [`JELLYFIN_HOST=${domain}`]; + return { envs, + domains, }; } diff --git a/apps/dokploy/templates/listmonk/docker-compose.yml b/apps/dokploy/templates/listmonk/docker-compose.yml index beabf4474..725d0a098 100644 --- a/apps/dokploy/templates/listmonk/docker-compose.yml +++ b/apps/dokploy/templates/listmonk/docker-compose.yml @@ -36,10 +36,6 @@ services: app: restart: unless-stopped image: listmonk/listmonk:v3.0.0 - ports: - - "${LISTMONK_PORT}" - networks: - - dokploy-network environment: - TZ=Etc/UTC depends_on: @@ -47,15 +43,7 @@ services: - setup volumes: - ../files/config.toml:/listmonk/config.toml - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${LISTMONK_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${LISTMONK_PORT}" volumes: listmonk-data: driver: local - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/listmonk/index.ts b/apps/dokploy/templates/listmonk/index.ts index d56d783df..725659ca4 100644 --- a/apps/dokploy/templates/listmonk/index.ts +++ b/apps/dokploy/templates/listmonk/index.ts @@ -1,20 +1,24 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generatePassword, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); const randomDomain = generateRandomDomain(schema); const adminPassword = generatePassword(32); + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 9000, + serviceName: "app", + }, + ]; + const envs = [ - `LISTMONK_HOST=${randomDomain}`, - "LISTMONK_PORT=9000", - `HASH=${mainServiceHash}`, `# login with admin:${adminPassword}`, "# check config.toml in Advanced / Volumes for more options", ]; @@ -48,5 +52,6 @@ params = "" return { envs, mounts, + domains, }; } diff --git a/apps/dokploy/templates/meilisearch/docker-compose.yml b/apps/dokploy/templates/meilisearch/docker-compose.yml index 3ce8f2124..ae5ebcb1a 100644 --- a/apps/dokploy/templates/meilisearch/docker-compose.yml +++ b/apps/dokploy/templates/meilisearch/docker-compose.yml @@ -1,25 +1,14 @@ -version: '3.8' +version: "3.8" services: meilisearch: - networks: - - dokploy-network image: getmeili/meilisearch:v1.8.3 - ports: - - ${MEILISEARCH_PORT} volumes: - meili_data:/meili_data environment: MEILI_MASTER_KEY: ${MEILI_MASTER_KEY} MEILI_ENV: ${MEILI_ENV} - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${MEILISEARCH_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${MEILISEARCH_PORT} volumes: meili_data: driver: local -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/meilisearch/index.ts b/apps/dokploy/templates/meilisearch/index.ts index afc009c0c..cfb8a9a4b 100644 --- a/apps/dokploy/templates/meilisearch/index.ts +++ b/apps/dokploy/templates/meilisearch/index.ts @@ -1,24 +1,26 @@ import { + type DomainSchema, type Schema, type Template, generateBase64, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const masterKey = generateBase64(32); - const envs = [ - `MEILISEARCH_HOST=${randomDomain}`, - "MEILISEARCH_PORT=7700", - "MEILI_ENV=development", - `MEILI_MASTER_KEY=${masterKey}`, - `HASH=${mainServiceHash}`, + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 7700, + serviceName: "meilisearch", + }, ]; + const envs = ["MEILI_ENV=development", `MEILI_MASTER_KEY=${masterKey}`]; return { envs, + domains, }; } diff --git a/apps/dokploy/templates/metabase/docker-compose.yml b/apps/dokploy/templates/metabase/docker-compose.yml index 3eee3344d..4dca4d015 100644 --- a/apps/dokploy/templates/metabase/docker-compose.yml +++ b/apps/dokploy/templates/metabase/docker-compose.yml @@ -4,8 +4,6 @@ services: image: metabase/metabase:v0.50.8 volumes: - /dev/urandom:/dev/random:ro - ports: - - ${METABASE_PORT} environment: MB_DB_TYPE: postgres MB_DB_DBNAME: metabaseappdb @@ -13,17 +11,11 @@ services: MB_DB_USER: metabase MB_DB_PASS: mysecretpassword MB_DB_HOST: postgres - networks: - - dokploy-network healthcheck: test: curl --fail -I http://localhost:3000/api/health || exit 1 interval: 15s timeout: 5s retries: 5 - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${METABASE_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${METABASE_PORT} postgres: image: postgres:14 environment: @@ -32,7 +24,3 @@ services: POSTGRES_PASSWORD: mysecretpassword networks: - dokploy-network - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/metabase/index.ts b/apps/dokploy/templates/metabase/index.ts index 1bd4281c6..0a08916e6 100644 --- a/apps/dokploy/templates/metabase/index.ts +++ b/apps/dokploy/templates/metabase/index.ts @@ -1,20 +1,22 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); const randomDomain = generateRandomDomain(schema); - const envs = [ - `METABASE_HOST=${randomDomain}`, - "METABASE_PORT=3000", - `HASH=${mainServiceHash}`, + + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 3000, + serviceName: "metabase", + }, ]; return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/minio/docker-compose.yml b/apps/dokploy/templates/minio/docker-compose.yml index 25701ea89..4b24bbcce 100644 --- a/apps/dokploy/templates/minio/docker-compose.yml +++ b/apps/dokploy/templates/minio/docker-compose.yml @@ -1,31 +1,13 @@ -version: '3.8' +version: "3.8" services: minio: image: minio/minio - ports: - - ${MINIO_API_PORT} - - ${MINIO_DASHBOARD_PORT} volumes: - minio-data:/data environment: - MINIO_ROOT_USER=minioadmin - MINIO_ROOT_PASSWORD=minioadmin123 command: server /data --console-address ":9001" - networks: - - dokploy-network - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.service=${HASH} - - traefik.http.routers.${HASH}.rule=Host(`${MINIO_DASHBOARD_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${MINIO_DASHBOARD_PORT} - # API router and service - - traefik.http.routers.${HASH}-api.service=${HASH}-api - - traefik.http.routers.${HASH}-api.rule=Host(`${MINIO_API_HOST}`) - - traefik.http.services.${HASH}-api.loadbalancer.server.port=${MINIO_API_PORT} volumes: minio-data: - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/minio/index.ts b/apps/dokploy/templates/minio/index.ts index 8e8b101ed..1345aafdc 100644 --- a/apps/dokploy/templates/minio/index.ts +++ b/apps/dokploy/templates/minio/index.ts @@ -1,23 +1,28 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const apiDomain = generateRandomDomain(schema); - const envs = [ - `MINIO_DASHBOARD_HOST=${randomDomain}`, - "MINIO_DASHBOARD_PORT=9001", - `MINIO_API_HOST=${apiDomain}`, - "MINIO_API_PORT=9000", - `HASH=${mainServiceHash}`, + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 9001, + serviceName: "minio", + }, + { + host: apiDomain, + port: 9000, + serviceName: "minio", + }, ]; return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/n8n/docker-compose.yml b/apps/dokploy/templates/n8n/docker-compose.yml index c26804dae..f8fb1f169 100644 --- a/apps/dokploy/templates/n8n/docker-compose.yml +++ b/apps/dokploy/templates/n8n/docker-compose.yml @@ -3,17 +3,9 @@ services: n8n: image: docker.n8n.io/n8nio/n8n:1.48.1 restart: always - networks: - - dokploy-network - ports: - - ${N8N_PORT} - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${N8N_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${N8N_PORT} environment: - N8N_HOST=${N8N_HOST} - - N8N_PORT=5678 + - N8N_PORT=${N8N_PORT} - N8N_PROTOCOL=http - NODE_ENV=production - WEBHOOK_URL=https://${N8N_HOST}/ @@ -23,7 +15,4 @@ services: - n8n_data:/home/node/.n8n volumes: - n8n_data: -networks: - dokploy-network: - external: true \ No newline at end of file + n8n_data: diff --git a/apps/dokploy/templates/n8n/index.ts b/apps/dokploy/templates/n8n/index.ts index 427ef80ed..da93c025f 100644 --- a/apps/dokploy/templates/n8n/index.ts +++ b/apps/dokploy/templates/n8n/index.ts @@ -1,21 +1,28 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 5678, + serviceName: "n8n", + }, + ]; const envs = [ - `N8N_HOST=${randomDomain}`, + `N8N_HOST=${mainDomain}`, "N8N_PORT=5678", - `HASH=${mainServiceHash}`, "GENERIC_TIMEZONE=Europe/Berlin", ]; return { envs, + domains, }; } diff --git a/apps/dokploy/templates/nocodb/docker-compose.yml b/apps/dokploy/templates/nocodb/docker-compose.yml index 60ecb57c7..726cf5e61 100644 --- a/apps/dokploy/templates/nocodb/docker-compose.yml +++ b/apps/dokploy/templates/nocodb/docker-compose.yml @@ -3,18 +3,10 @@ services: nocodb: image: nocodb/nocodb:0.251.1 restart: always - networks: - - dokploy-network - ports: - - ${NOCODB_PORT} environment: - NC_DB : "pg://root_db?u=postgres&p=password&d=root_db" - PORT : ${NOCODB_PORT} + NC_DB: "pg://root_db?u=postgres&p=password&d=root_db" + PORT: ${NOCODB_PORT} NC_REDIS_URL: ${NC_REDIS_URL} - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${NOCODB_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${NOCODB_PORT} volumes: - nc_data:/usr/app/data @@ -30,15 +22,11 @@ services: healthcheck: interval: 10s retries: 10 - test: "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\"" + test: 'pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"' timeout: 2s volumes: - "db_data:/var/lib/postgresql/data" -networks: - dokploy-network: - external: true - volumes: db_data: {} - nc_data: {} \ No newline at end of file + nc_data: {} diff --git a/apps/dokploy/templates/nocodb/index.ts b/apps/dokploy/templates/nocodb/index.ts index 4b52920dc..60620dbd4 100644 --- a/apps/dokploy/templates/nocodb/index.ts +++ b/apps/dokploy/templates/nocodb/index.ts @@ -1,26 +1,28 @@ // EXAMPLE import { + type DomainSchema, type Schema, type Template, generateBase64, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); const randomDomain = generateRandomDomain(schema); const secretBase = generateBase64(64); - const toptKeyBase = generateBase64(32); - const envs = [ - `NOCODB_HOST=${randomDomain}`, - "NOCODB_PORT=8000", - `NC_AUTH_JWT_SECRET=${secretBase}`, - `HASH=${mainServiceHash}`, + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 8000, + serviceName: "nocodb", + }, ]; + const envs = ["NOCODB_PORT=8000", `NC_AUTH_JWT_SECRET=${secretBase}`]; + return { envs, + domains, }; } diff --git a/apps/dokploy/templates/odoo/docker-compose.yml b/apps/dokploy/templates/odoo/docker-compose.yml index e6e2a7242..80b34f0c0 100644 --- a/apps/dokploy/templates/odoo/docker-compose.yml +++ b/apps/dokploy/templates/odoo/docker-compose.yml @@ -2,20 +2,12 @@ version: "3.8" services: web: image: odoo:16.0 - networks: - - dokploy-network depends_on: - db - ports: - - ${ODOO_PORT} environment: - HOST=db - USER=odoo - PASSWORD=odoo - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${ODOO_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${ODOO_PORT}" volumes: - odoo-web-data:/var/lib/odoo - ../files/config:/etc/odoo @@ -35,7 +27,3 @@ services: volumes: odoo-web-data: odoo-db-data: - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/odoo/index.ts b/apps/dokploy/templates/odoo/index.ts index 346e322b1..904293c11 100644 --- a/apps/dokploy/templates/odoo/index.ts +++ b/apps/dokploy/templates/odoo/index.ts @@ -1,20 +1,22 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); const randomDomain = generateRandomDomain(schema); - const envs = [ - `ODOO_HOST=${randomDomain}`, - "ODOO_PORT=8069", - `HASH=${mainServiceHash}`, + + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 8069, + serviceName: "web", + }, ]; return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/open-webui/docker-compose.yml b/apps/dokploy/templates/open-webui/docker-compose.yml index 457a60bd4..d396dacc0 100644 --- a/apps/dokploy/templates/open-webui/docker-compose.yml +++ b/apps/dokploy/templates/open-webui/docker-compose.yml @@ -1,6 +1,5 @@ -version: '3.8' +version: "3.8" services: - ollama: volumes: - ollama:/root/.ollama @@ -13,24 +12,14 @@ services: open-webui: image: ghcr.io/open-webui/open-webui:${WEBUI_DOCKER_TAG-main} - networks: - - dokploy-network volumes: - open-webui:/app/backend/data depends_on: - ollama environment: - - 'OLLAMA_BASE_URL=http://ollama:11434' - - 'WEBUI_SECRET_KEY=' + - "OLLAMA_BASE_URL=http://ollama:11434" + - "WEBUI_SECRET_KEY=" restart: unless-stopped - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${OPEN_WEBUI_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${OPEN_WEBUI_PORT}" - -networks: - dokploy-network: - external: true volumes: ollama: {} diff --git a/apps/dokploy/templates/open-webui/index.ts b/apps/dokploy/templates/open-webui/index.ts index 931e0ec84..0431c2a11 100644 --- a/apps/dokploy/templates/open-webui/index.ts +++ b/apps/dokploy/templates/open-webui/index.ts @@ -1,22 +1,24 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); const randomDomain = generateRandomDomain(schema); - const envs = [ - `OPEN_WEBUI_HOST=${randomDomain}`, - "OPEN_WEBUI_PORT=8080", - `HASH=${mainServiceHash}`, - "OLLAMA_DOCKER_TAG=0.1.47", - "WEBUI_DOCKER_TAG=0.3.7", + + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 8080, + serviceName: "open-webui", + }, ]; + const envs = ["OLLAMA_DOCKER_TAG=0.1.47", "WEBUI_DOCKER_TAG=0.3.7"]; return { envs, + domains, }; } diff --git a/apps/dokploy/templates/phpmyadmin/docker-compose.yml b/apps/dokploy/templates/phpmyadmin/docker-compose.yml index c6743a580..1f775f09a 100644 --- a/apps/dokploy/templates/phpmyadmin/docker-compose.yml +++ b/apps/dokploy/templates/phpmyadmin/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.8' +version: "3.8" services: db: @@ -20,21 +20,9 @@ services: PMA_USER: ${MYSQL_USER} PMA_PASSWORD: ${MYSQL_PASSWORD} PMA_ARBITRARY: 1 - ports: - - ${PHPMYADMIN_PORT} depends_on: - db - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${PHPMYADMIN_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${PHPMYADMIN_PORT} - networks: - - dokploy-network volumes: db_data: driver: local - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/phpmyadmin/index.ts b/apps/dokploy/templates/phpmyadmin/index.ts index 1bae5ed02..e1c976b9d 100644 --- a/apps/dokploy/templates/phpmyadmin/index.ts +++ b/apps/dokploy/templates/phpmyadmin/index.ts @@ -1,20 +1,24 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generatePassword, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const rootPassword = generatePassword(32); const password = generatePassword(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 80, + serviceName: "phpmyadmin", + }, + ]; const envs = [ - `PHPMYADMIN_HOST=${randomDomain}`, - "PHPMYADMIN_PORT=80", - `HASH=${mainServiceHash}`, `MYSQL_ROOT_PASSWORD=${rootPassword}`, "MYSQL_DATABASE=mysql", "MYSQL_USER=phpmyadmin", @@ -23,5 +27,6 @@ export function generate(schema: Schema): Template { return { envs, + domains, }; } diff --git a/apps/dokploy/templates/plausible/docker-compose.yml b/apps/dokploy/templates/plausible/docker-compose.yml index 350cd87c5..62ce5ece4 100644 --- a/apps/dokploy/templates/plausible/docker-compose.yml +++ b/apps/dokploy/templates/plausible/docker-compose.yml @@ -32,17 +32,9 @@ services: depends_on: - plausible_db - plausible_events_db - ports: - - ${PLAUSIBLE_PORT} - networks: - - dokploy-network env_file: - .env - labels: - - "traefik.enable=true" - - "traefik.http.routers.${HASH}.rule=Host(`${PLAUSIBLE_HOST}`)" - - "traefik.http.services.${HASH}.loadbalancer.server.port=${PLAUSIBLE_PORT}" volumes: db-data: driver: local @@ -50,7 +42,3 @@ volumes: driver: local event-logs: driver: local - -networks: - dokploy-network: - external: true diff --git a/apps/dokploy/templates/plausible/index.ts b/apps/dokploy/templates/plausible/index.ts index 643c5df05..2bd1212b6 100644 --- a/apps/dokploy/templates/plausible/index.ts +++ b/apps/dokploy/templates/plausible/index.ts @@ -1,24 +1,28 @@ import { + type DomainSchema, type Schema, type Template, generateBase64, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); const secretBase = generateBase64(64); const toptKeyBase = generateBase64(32); + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 8000, + serviceName: "plausible", + }, + ]; + const envs = [ - `PLAUSIBLE_HOST=${randomDomain}`, - "PLAUSIBLE_PORT=8000", - `BASE_URL=http://${randomDomain}`, + `BASE_URL=http://${mainDomain}`, `SECRET_KEY_BASE=${secretBase}`, `TOTP_VAULT_KEY=${toptKeyBase}`, - `HASH=${mainServiceHash}`, ]; const mounts: Template["mounts"] = [ @@ -62,5 +66,6 @@ export function generate(schema: Schema): Template { return { envs, mounts, + domains, }; } diff --git a/apps/dokploy/templates/pocketbase/docker-compose.yml b/apps/dokploy/templates/pocketbase/docker-compose.yml index 7570dd585..fa6674afe 100644 --- a/apps/dokploy/templates/pocketbase/docker-compose.yml +++ b/apps/dokploy/templates/pocketbase/docker-compose.yml @@ -3,19 +3,7 @@ services: pocketbase: image: spectado/pocketbase:0.22.12 restart: unless-stopped - ports: - - ${POCKETBASE_PORT} - networks: - - dokploy-network - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${POCKETBASE_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${POCKETBASE_PORT} volumes: - /etc/dokploy/templates/${HASH}/data:/pb_data - /etc/dokploy/templates/${HASH}/public:/pb_public - /etc/dokploy/templates/${HASH}/migrations:/pb_migrations - -networks: - dokploy-network: - external: true \ No newline at end of file diff --git a/apps/dokploy/templates/pocketbase/index.ts b/apps/dokploy/templates/pocketbase/index.ts index 1a5e964dc..f9fc7f8f0 100644 --- a/apps/dokploy/templates/pocketbase/index.ts +++ b/apps/dokploy/templates/pocketbase/index.ts @@ -1,21 +1,22 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); - const envs = [ - `POCKETBASE_HOST=${randomDomain}`, - "POCKETBASE_PORT=80", - `HASH=${mainServiceHash}`, + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 80, + serviceName: "pocketbase", + }, ]; return { - envs, + domains, }; } diff --git a/apps/dokploy/templates/rocketchat/docker-compose.yml b/apps/dokploy/templates/rocketchat/docker-compose.yml index 78056b9a7..751bd845c 100644 --- a/apps/dokploy/templates/rocketchat/docker-compose.yml +++ b/apps/dokploy/templates/rocketchat/docker-compose.yml @@ -9,18 +9,10 @@ services: ROOT_URL: ${ROOT_URL:-http://${ROCKETCHAT_HOST}:${ROCKETCHAT_PORT}} PORT: ${ROCKETCHAT_PORT} DEPLOY_METHOD: docker - DEPLOY_PLATFORM: - REG_TOKEN: + DEPLOY_PLATFORM: + REG_TOKEN: depends_on: - mongodb - ports: - - ${ROCKETCHAT_PORT} - networks: - - dokploy-network - labels: - - traefik.enable=true - - traefik.http.routers.${HASH}.rule=Host(`${ROCKETCHAT_HOST}`) - - traefik.http.services.${HASH}.loadbalancer.server.port=${ROCKETCHAT_PORT} mongodb: image: docker.io/bitnami/mongodb:5.0 @@ -41,8 +33,3 @@ services: volumes: mongodb_data: { driver: local } - - -networks: - dokploy-network: - external: true \ No newline at end of file diff --git a/apps/dokploy/templates/rocketchat/index.ts b/apps/dokploy/templates/rocketchat/index.ts index 768cf46fe..0c10307a2 100644 --- a/apps/dokploy/templates/rocketchat/index.ts +++ b/apps/dokploy/templates/rocketchat/index.ts @@ -1,21 +1,25 @@ import { + type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); - const randomDomain = generateRandomDomain(schema); + const mainDomain = generateRandomDomain(schema); - const envs = [ - `ROCKETCHAT_HOST=${randomDomain}`, - "ROCKETCHAT_PORT=3000", - `HASH=${mainServiceHash}`, + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "rocketchat", + }, ]; + const envs = [`ROCKETCHAT_HOST=${mainDomain}`, "ROCKETCHAT_PORT=3000"]; + return { envs, + domains, }; } diff --git a/apps/dokploy/templates/soketi/docker-compose.yml b/apps/dokploy/templates/soketi/docker-compose.yml new file mode 100644 index 000000000..1784cdc79 --- /dev/null +++ b/apps/dokploy/templates/soketi/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3" + +services: + soketi: + image: quay.io/soketi/soketi:1.4-16-debian + container_name: soketi + environment: + SOKETI_DEBUG: "1" + SOKETI_HOST: "0.0.0.0" + SOKETI_PORT: "6001" + SOKETI_METRICS_SERVER_PORT: "9601" + restart: unless-stopped diff --git a/apps/dokploy/templates/soketi/index.ts b/apps/dokploy/templates/soketi/index.ts new file mode 100644 index 000000000..47aa461df --- /dev/null +++ b/apps/dokploy/templates/soketi/index.ts @@ -0,0 +1,28 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const metricsDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 6001, + serviceName: "soketi", + }, + { + host: metricsDomain, + port: 9601, + serviceName: "soketi", + }, + ]; + + return { + domains, + }; +} diff --git a/apps/dokploy/templates/supabase/docker-compose.yml b/apps/dokploy/templates/supabase/docker-compose.yml new file mode 100644 index 000000000..5be8158c0 --- /dev/null +++ b/apps/dokploy/templates/supabase/docker-compose.yml @@ -0,0 +1,437 @@ +# Usage +# Start: docker compose up +# With helpers: docker compose -f docker-compose.yml -f ../files/dev/docker-compose.dev.yml up +# Stop: docker compose down +# Destroy: docker compose -f docker-compose.yml -f ../files/dev/docker-compose.dev.yml down -v --remove-orphans + +name: supabase +version: "3.8" + +services: + studio: + container_name: supabase-studio + image: supabase/studio:20240729-ce42139 + networks: + - dokploy-network + restart: unless-stopped + healthcheck: + test: [ "CMD", "node", "-e", "require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" ] + timeout: 5s + interval: 5s + retries: 3 + depends_on: + analytics: + condition: service_healthy + environment: + STUDIO_PG_META_URL: http://meta:8080 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + + DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION} + DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT} + + SUPABASE_URL: http://kong:8000 + SUPABASE_PUBLIC_URL: http://${SUPABASE_HOST} + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + AUTH_JWT_SECRET: ${JWT_SECRET} + + LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_URL: http://analytics:4000 + NEXT_PUBLIC_ENABLE_LOGS: true + # Comment to use Big Query backend for analytics + NEXT_ANALYTICS_BACKEND_PROVIDER: postgres + # Uncomment to use Big Query backend for analytics + # NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery + + kong: + container_name: supabase-kong + image: kong:2.8.1 + restart: unless-stopped + networks: + - dokploy-network + # https://unix.stackexchange.com/a/294837 + entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start' + #ports: + # - ${KONG_HTTP_PORT}:8000/tcp + # - ${KONG_HTTPS_PORT}:8443/tcp + expose: + - 8000 + - 8443 + labels: + - traefik.enable=true + - traefik.http.routers.frontend-app.rule=Host(`${SUPABASE_HOST}`) + - traefik.http.routers.frontend-app.entrypoints=web + - traefik.http.services.frontend-app.loadbalancer.server.port=${KONG_HTTP_PORT} + depends_on: + analytics: + condition: service_healthy + environment: + KONG_DATABASE: "off" + KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml + # https://github.com/supabase/cli/issues/14 + KONG_DNS_ORDER: LAST,A,CNAME + KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth + KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k + KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + DASHBOARD_USERNAME: ${DASHBOARD_USERNAME} + DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD} + volumes: + # https://github.com/supabase/supabase/issues/12661 + - ../files/volumes/api/kong.yml:/home/kong/temp.yml:ro + + auth: + container_name: supabase-auth + image: supabase/gotrue:v2.158.1 + networks: + - dokploy-network + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + healthcheck: + test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9999/health" ] + timeout: 5s + interval: 5s + retries: 3 + restart: unless-stopped + environment: + GOTRUE_API_HOST: 0.0.0.0 + GOTRUE_API_PORT: 9999 + API_EXTERNAL_URL: http://${SUPABASE_HOST} + + GOTRUE_DB_DRIVER: postgres + GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOSTNAME}:${POSTGRES_PORT}/${POSTGRES_DB} + + GOTRUE_SITE_URL: http://${SUPABASE_HOST} + GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS} + GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP} + + GOTRUE_JWT_ADMIN_ROLES: service_role + GOTRUE_JWT_AUD: authenticated + GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated + GOTRUE_JWT_EXP: ${JWT_EXPIRY} + GOTRUE_JWT_SECRET: ${JWT_SECRET} + + GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} + GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS} + GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM} + # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true + # GOTRUE_SMTP_MAX_FREQUENCY: 1s + GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL} + GOTRUE_SMTP_HOST: ${SMTP_HOSTNAME} + GOTRUE_SMTP_PORT: ${SMTP_PORT} + GOTRUE_SMTP_USER: ${SMTP_USER} + GOTRUE_SMTP_PASS: ${SMTP_PASS} + GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME} + GOTRUE_MAILER_URLPATHS_INVITE: ${MAILER_URLPATHS_INVITE} + GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${MAILER_URLPATHS_CONFIRMATION} + GOTRUE_MAILER_URLPATHS_RECOVERY: ${MAILER_URLPATHS_RECOVERY} + GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${MAILER_URLPATHS_EMAIL_CHANGE} + + GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP} + GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM} + # Uncomment to enable custom access token hook. You'll need to create a public.custom_access_token_hook function and grant necessary permissions. + # See: https://supabase.com/docs/guides/auth/auth-hooks#hook-custom-access-token for details + # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED="true" + # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI="pg-functions://postgres/public/custom_access_token_hook" + + # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED="true" + # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI="pg-functions://postgres/public/mfa_verification_attempt" + + # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED="true" + # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI="pg-functions://postgres/public/password_verification_attempt" + + + + + rest: + container_name: supabase-rest + image: postgrest/postgrest:v12.2.0 + networks: + - dokploy-network + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + restart: unless-stopped + environment: + PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOSTNAME}:${POSTGRES_PORT}/${POSTGRES_DB} + PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_ANON_ROLE: anon + PGRST_JWT_SECRET: ${JWT_SECRET} + PGRST_DB_USE_LEGACY_GUCS: "false" + PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET} + PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY} + command: "postgrest" + + realtime: + # This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain + container_name: realtime-dev.supabase-realtime + image: supabase/realtime:v2.30.23 + networks: + - dokploy-network + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "-sSfL", "--head", "-o", "/dev/null", "-H", "Authorization: Bearer ${ANON_KEY}", "http://localhost:4000/api/tenants/realtime-dev/health" ] + timeout: 5s + interval: 5s + retries: 3 + restart: unless-stopped + environment: + PORT: 4000 + DB_HOST: ${POSTGRES_HOSTNAME} + DB_PORT: ${POSTGRES_PORT} + DB_USER: supabase_admin + DB_PASSWORD: ${POSTGRES_PASSWORD} + DB_NAME: ${POSTGRES_DB} + DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime' + DB_ENC_KEY: supabaserealtime + API_JWT_SECRET: ${JWT_SECRET} + SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq + ERL_AFLAGS: -proto_dist inet_tcp + DNS_NODES: "''" + RLIMIT_NOFILE: "10000" + APP_NAME: realtime + SEED_SELF_HOST: true + + # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up + storage: + container_name: supabase-storage + image: supabase/storage-api:v1.0.6 + networks: + - dokploy-network + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + rest: + condition: service_started + imgproxy: + condition: service_started + healthcheck: + test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5000/status" ] + timeout: 5s + interval: 5s + retries: 3 + restart: unless-stopped + environment: + ANON_KEY: ${ANON_KEY} + SERVICE_KEY: ${SERVICE_ROLE_KEY} + POSTGREST_URL: http://rest:3000 + PGRST_JWT_SECRET: ${JWT_SECRET} + DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOSTNAME}:${POSTGRES_PORT}/${POSTGRES_DB} + FILE_SIZE_LIMIT: 52428800 + STORAGE_BACKEND: file + FILE_STORAGE_BACKEND_PATH: /var/lib/storage + TENANT_ID: stub + # TODO: https://github.com/supabase/storage-api/issues/55 + REGION: stub + GLOBAL_S3_BUCKET: stub + ENABLE_IMAGE_TRANSFORMATION: "true" + IMGPROXY_URL: http://imgproxy:5001 + volumes: + - ../files/volumes/storage:/var/lib/storage:z + + imgproxy: + container_name: supabase-imgproxy + image: darthsim/imgproxy:v3.8.0 + networks: + - dokploy-network + healthcheck: + test: [ "CMD", "imgproxy", "health" ] + timeout: 5s + interval: 5s + retries: 3 + environment: + IMGPROXY_BIND: ":5001" + IMGPROXY_LOCAL_FILESYSTEM_ROOT: / + IMGPROXY_USE_ETAG: "true" + IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION} + volumes: + - ../files/volumes/storage:/var/lib/storage:z + + meta: + container_name: supabase-meta + image: supabase/postgres-meta:v0.83.2 + networks: + - dokploy-network + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + restart: unless-stopped + environment: + PG_META_PORT: 8080 + PG_META_DB_HOST: ${POSTGRES_HOSTNAME} + PG_META_DB_PORT: ${POSTGRES_PORT} + PG_META_DB_NAME: ${POSTGRES_DB} + PG_META_DB_USER: supabase_admin + PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} + + functions: + container_name: supabase-edge-functions + image: supabase/edge-runtime:v1.56.0 + restart: unless-stopped + networks: + - dokploy-network + depends_on: + analytics: + condition: service_healthy + environment: + JWT_SECRET: ${JWT_SECRET} + SUPABASE_URL: http://kong:8000 + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOSTNAME}:${POSTGRES_PORT}/${POSTGRES_DB} + # TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786 + VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}" + volumes: + - ../files/volumes/functions:/home/deno/functions:Z + command: + - start + - --main-service + - /home/deno/functions/main + + analytics: + container_name: supabase-analytics + image: supabase/logflare:1.4.0 + networks: + - dokploy-network + healthcheck: + test: [ "CMD", "curl", "http://localhost:4000/health" ] + timeout: 5s + interval: 5s + retries: 10 + restart: unless-stopped + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + # Uncomment to use Big Query backend for analytics + # volumes: + # - type: bind + # source: ${PWD}/gcloud.json + # target: /opt/app/rel/logflare/bin/gcloud.json + # read_only: true + environment: + LOGFLARE_NODE_HOST: 127.0.0.1 + DB_USERNAME: supabase_admin + DB_DATABASE: ${POSTGRES_DB} + DB_HOSTNAME: ${POSTGRES_HOSTNAME} + DB_PORT: ${POSTGRES_PORT} + DB_PASSWORD: ${POSTGRES_PASSWORD} + DB_SCHEMA: _analytics + LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_SINGLE_TENANT: true + LOGFLARE_SUPABASE_MODE: true + LOGFLARE_MIN_CLUSTER_SIZE: 1 + + # Comment variables to use Big Query backend for analytics + POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOSTNAME}:${POSTGRES_PORT}/${POSTGRES_DB} + POSTGRES_BACKEND_SCHEMA: _analytics + LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true + # Uncomment to use Big Query backend for analytics + # GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID} + # GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER} + #ports: + # - 4000:4000 + expose: + - 4000 + + # Comment out everything below this point if you are using an external Postgres database + db: + container_name: supabase-db + image: supabase/postgres:15.1.1.78 + networks: + - dokploy-network + healthcheck: + test: pg_isready -U postgres -h localhost + interval: 5s + timeout: 5s + retries: 10 + depends_on: + vector: + condition: service_healthy + command: + - postgres + - -c + - config_file=/etc/postgresql/postgresql.conf + - -c + - log_min_messages=fatal # prevents Realtime polling queries from appearing in logs + restart: unless-stopped + #ports: + # # Pass down internal port because it's set dynamically by other services + # - ${POSTGRES_PORT}:${POSTGRES_PORT} + expose: + - ${POSTGRES_PORT} + environment: + POSTGRES_HOST: /var/run/postgresql + PGPORT: ${POSTGRES_PORT} + POSTGRES_PORT: ${POSTGRES_PORT} + PGPASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + PGDATABASE: ${POSTGRES_DB} + POSTGRES_DB: ${POSTGRES_DB} + JWT_SECRET: ${JWT_SECRET} + JWT_EXP: ${JWT_EXPIRY} + volumes: + - ../files/volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z + # Must be superuser to create event trigger + - ../files/volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z + # Must be superuser to alter reserved role + - ../files/volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z + # Initialize the database settings with JWT_SECRET and JWT_EXP + - ../files/volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z + # PGDATA directory is persisted between restarts + - ../files/volumes/db/data:/var/lib/postgresql/data:Z + # Changes required for Analytics support + - ../files/volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z + # Use named volume to persist pgsodium decryption key between restarts + - db-config:/etc/postgresql-custom + + vector: + container_name: supabase-vector + image: timberio/vector:0.28.1-alpine + networks: + - dokploy-network + healthcheck: + test: + [ + + "CMD", + "wget", + "--no-verbose", + "--tries=1", + "--spider", + "http://vector:9001/health" + ] + timeout: 5s + interval: 5s + retries: 3 + volumes: + - ../files/volumes/logs/vector.yml:/etc/vector/vector.yml:ro + - ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro + environment: + LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + command: [ "--config", "etc/vector/vector.yml" ] + +volumes: + db-config: + + +networks: + dokploy-network: + external: true diff --git a/apps/dokploy/templates/supabase/index.ts b/apps/dokploy/templates/supabase/index.ts new file mode 100644 index 000000000..2456f015f --- /dev/null +++ b/apps/dokploy/templates/supabase/index.ts @@ -0,0 +1,989 @@ +import { createHmac, randomBytes } from "node:crypto"; +import { + type Schema, + type Template, + generateBase64, + generateHash, + generatePassword, + generateRandomDomain, +} from "../utils"; + +interface JWTPayload { + role: "anon" | "service_role"; + iss: string; + iat: number; + exp: number; +} + +function base64UrlEncode(str: string): string { + return Buffer.from(str) + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); +} + +function generateJWT(payload: JWTPayload, secret: string): string { + const header = { alg: "HS256", typ: "JWT" }; + + const encodedHeader = base64UrlEncode(JSON.stringify(header)); + const encodedPayload = base64UrlEncode(JSON.stringify(payload)); + + const signature = createHmac("sha256", secret) + .update(`${encodedHeader}.${encodedPayload}`) + .digest("base64url"); + + return `${encodedHeader}.${encodedPayload}.${signature}`; +} + +export function generateSupabaseAnonJWT(secret: string): string { + const now = Math.floor(Date.now() / 1000); + const payload: JWTPayload = { + role: "anon", + iss: "supabase", + iat: now, + exp: now + 100 * 365 * 24 * 60 * 60, // 100 years + }; + + return generateJWT(payload, secret); +} + +export function generateSupabaseServiceJWT(secret: string): string { + const now = Math.floor(Date.now() / 1000); + const payload: JWTPayload = { + role: "service_role", + iss: "supabase", + iat: now, + exp: now + 100 * 365 * 24 * 60 * 60, // 100 years + }; + + return generateJWT(payload, secret); +} + +export function generate(schema: Schema): Template { + const mainServiceHash = generateHash(schema.projectName); + const randomDomain = generateRandomDomain(schema); + + const postgresPassword = generatePassword(32); + const jwtSecret = generateBase64(32); + const dashboardPassword = generatePassword(32); + const logflareApiKey = generatePassword(32); + + const annonKey = generateSupabaseAnonJWT(jwtSecret); + const serviceRoleKey = generateSupabaseServiceJWT(jwtSecret); + + const envs = [ + `SUPABASE_HOST=${randomDomain}`, + `POSTGRES_PASSWORD=${postgresPassword}`, + `JWT_SECRET=${jwtSecret}`, + `ANON_KEY=${annonKey}`, + `SERVICE_ROLE_KEY=${serviceRoleKey}`, + "DASHBOARD_USERNAME=supabase", + `DASHBOARD_PASSWORD=${dashboardPassword}`, + "POSTGRES_HOSTNAME=db", + "POSTGRES_DB=postgres", + "POSTGRES_PORT=5432", + "KONG_HTTP_PORT=8000", + "KONG_HTTPS_PORT=8443", + "PGRST_DB_SCHEMAS=public,storage,graphql_public", + "ADDITIONAL_REDIRECT_URLS=", + "JWT_EXPIRY=3600", + "DISABLE_SIGNUP=false", + `MAILER_URLPATHS_CONFIRMATION=\"/auth/v1/verify\"`, + `MAILER_URLPATHS_INVITE=\"/auth/v1/verify\"`, + `MAILER_URLPATHS_RECOVERY=\"/auth/v1/verify\"`, + `MAILER_URLPATHS_EMAIL_CHANGE=\"/auth/v1/verify\"`, + "ENABLE_EMAIL_SIGNUP=true", + "ENABLE_EMAIL_AUTOCONFIRM=false", + "SMTP_ADMIN_EMAIL=admin@example.com", + "SMTP_HOSTNAME=supabase-mail", + "SMTP_PORT=2500", + "SMTP_USER=fake_mail_user", + "SMTP_PASS=fake_mail_password", + "SMTP_SENDER_NAME=fake_sender", + "ENABLE_ANONYMOUS_USERS=false", + "ENABLE_PHONE_SIGNUP=true", + "ENABLE_PHONE_AUTOCONFIRM=true", + "STUDIO_DEFAULT_ORGANIZATION=Default Organization", + "STUDIO_DEFAULT_PROJECT=Default Project", + "STUDIO_PORT=3000", + "IMGPROXY_ENABLE_WEBP_DETECTION=true", + "FUNCTIONS_VERIFY_JWT=false", + `LOGFLARE_LOGGER_BACKEND_API_KEY=${logflareApiKey}`, + `LOGFLARE_API_KEY=${logflareApiKey}`, + "DOCKER_SOCKET_LOCATION=/var/run/docker.sock", + "GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID", + "GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER", + `HASH=${mainServiceHash}`, + ]; + + const mounts: Template["mounts"] = [ + { + filePath: "/volumes/api/kong.yml", + content: ` +_format_version: '2.1' +_transform: true + +### +### Consumers / Users +### +consumers: + - username: DASHBOARD + - username: anon + keyauth_credentials: + - key: $SUPABASE_ANON_KEY + - username: service_role + keyauth_credentials: + - key: $SUPABASE_SERVICE_KEY + +### +### Access Control List +### +acls: + - consumer: anon + group: anon + - consumer: service_role + group: admin + +### +### Dashboard credentials +### +basicauth_credentials: + - consumer: DASHBOARD + username: $DASHBOARD_USERNAME + password: $DASHBOARD_PASSWORD + +### +### API Routes +### +services: + ## Open Auth routes + - name: auth-v1-open + url: http://auth:9999/verify + routes: + - name: auth-v1-open + strip_path: true + paths: + - /auth/v1/verify + plugins: + - name: cors + - name: auth-v1-open-callback + url: http://auth:9999/callback + routes: + - name: auth-v1-open-callback + strip_path: true + paths: + - /auth/v1/callback + plugins: + - name: cors + - name: auth-v1-open-authorize + url: http://auth:9999/authorize + routes: + - name: auth-v1-open-authorize + strip_path: true + paths: + - /auth/v1/authorize + plugins: + - name: cors + + ## Secure Auth routes + - name: auth-v1 + _comment: 'GoTrue: /auth/v1/* -> http://auth:9999/*' + url: http://auth:9999/ + routes: + - name: auth-v1-all + strip_path: true + paths: + - /auth/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure REST routes + - name: rest-v1 + _comment: 'PostgREST: /rest/v1/* -> http://rest:3000/*' + url: http://rest:3000/ + routes: + - name: rest-v1-all + strip_path: true + paths: + - /rest/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: true + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure GraphQL routes + - name: graphql-v1 + _comment: 'PostgREST: /graphql/v1/* -> http://rest:3000/rpc/graphql' + url: http://rest:3000/rpc/graphql + routes: + - name: graphql-v1-all + strip_path: true + paths: + - /graphql/v1 + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: true + - name: request-transformer + config: + add: + headers: + - Content-Profile:graphql_public + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure Realtime routes + - name: realtime-v1-ws + _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' + url: http://realtime-dev.supabase-realtime:4000/socket + protocol: ws + routes: + - name: realtime-v1-ws + strip_path: true + paths: + - /realtime/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + - name: realtime-v1-rest + _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' + url: http://realtime-dev.supabase-realtime:4000/api + protocol: http + routes: + - name: realtime-v1-rest + strip_path: true + paths: + - /realtime/v1/api + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + ## Storage routes: the storage server manages its own auth + - name: storage-v1 + _comment: 'Storage: /storage/v1/* -> http://storage:5000/*' + url: http://storage:5000/ + routes: + - name: storage-v1-all + strip_path: true + paths: + - /storage/v1/ + plugins: + - name: cors + + ## Edge Functions routes + - name: functions-v1 + _comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*' + url: http://functions:9000/ + routes: + - name: functions-v1-all + strip_path: true + paths: + - /functions/v1/ + plugins: + - name: cors + + ## Analytics routes + - name: analytics-v1 + _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*' + url: http://analytics:4000/ + routes: + - name: analytics-v1-all + strip_path: true + paths: + - /analytics/v1/ + + ## Secure Database routes + - name: meta + _comment: 'pg-meta: /pg/* -> http://pg-meta:8080/*' + url: http://meta:8080/ + routes: + - name: meta-all + strip_path: true + paths: + - /pg/ + plugins: + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + + ## Protected Dashboard - catch all remaining routes + - name: dashboard + _comment: 'Studio: /* -> http://studio:3000/*' + url: http://studio:3000/ + routes: + - name: dashboard-all + strip_path: true + paths: + - / + plugins: + - name: cors + - name: basic-auth + config: + hide_credentials: true + `, + }, + { + filePath: "/volumes/db/init/data.sql", + content: ` + `, + }, + { + filePath: "/volumes/db/jwt.sql", + content: ` +\\set jwt_secret \`echo "$JWT_SECRET"\` +\\set jwt_exp \`echo "$JWT_EXP"\` + +ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :'jwt_secret'; +ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :'jwt_exp'; + `, + }, + { + filePath: "/volumes/db/logs.sql", + content: ` +\\set pguser \`echo "$POSTGRES_USER"\` + +create schema if not exists _analytics; +alter schema _analytics owner to :pguser; + `, + }, + { + filePath: "/volumes/db/realtime.sql", + content: ` +\\set pguser \`echo "$POSTGRES_USER"\` + +create schema if not exists _realtime; +alter schema _realtime owner to :pguser; + `, + }, + { + filePath: "/volumes/db/roles.sql", + content: ` +-- NOTE: change to your own passwords for production environments +\\set pgpass \`echo "$POSTGRES_PASSWORD"\` + +ALTER USER authenticator WITH PASSWORD :'pgpass'; +ALTER USER pgbouncer WITH PASSWORD :'pgpass'; +ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass'; +ALTER USER supabase_functions_admin WITH PASSWORD :'pgpass'; +ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; + `, + }, + { + filePath: "/volumes/db/webhooks.sql", + content: ` +BEGIN; + -- Create pg_net extension + CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions; + -- Create supabase_functions schema + CREATE SCHEMA supabase_functions AUTHORIZATION supabase_admin; + GRANT USAGE ON SCHEMA supabase_functions TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role; + -- supabase_functions.migrations definition + CREATE TABLE supabase_functions.migrations ( + version text PRIMARY KEY, + inserted_at timestamptz NOT NULL DEFAULT NOW() + ); + -- Initial supabase_functions migration + INSERT INTO supabase_functions.migrations (version) VALUES ('initial'); + -- supabase_functions.hooks definition + CREATE TABLE supabase_functions.hooks ( + id bigserial PRIMARY KEY, + hook_table_id integer NOT NULL, + hook_name text NOT NULL, + created_at timestamptz NOT NULL DEFAULT NOW(), + request_id bigint + ); + CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); + CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); + COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; + CREATE FUNCTION supabase_functions.http_request() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END + $function$; + -- Supabase super admin + DO + $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_roles + WHERE rolname = 'supabase_functions_admin' + ) + THEN + CREATE USER supabase_functions_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; + END IF; + END + $$; + GRANT ALL PRIVILEGES ON SCHEMA supabase_functions TO supabase_functions_admin; + GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA supabase_functions TO supabase_functions_admin; + GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA supabase_functions TO supabase_functions_admin; + ALTER USER supabase_functions_admin SET search_path = "supabase_functions"; + ALTER table "supabase_functions".migrations OWNER TO supabase_functions_admin; + ALTER table "supabase_functions".hooks OWNER TO supabase_functions_admin; + ALTER function "supabase_functions".http_request() OWNER TO supabase_functions_admin; + GRANT supabase_functions_admin TO postgres; + -- Remove unused supabase_pg_net_admin role + DO + $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_roles + WHERE rolname = 'supabase_pg_net_admin' + ) + THEN + REASSIGN OWNED BY supabase_pg_net_admin TO supabase_admin; + DROP OWNED BY supabase_pg_net_admin; + DROP ROLE supabase_pg_net_admin; + END IF; + END + $$; + -- pg_net grants when extension is already enabled + DO + $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_extension + WHERE extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; + END + $$; + -- Event trigger for pg_net + CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; + END; + $$; + COMMENT ON FUNCTION extensions.grant_pg_net_access IS 'Grants access to pg_net'; + DO + $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_event_trigger + WHERE evtname = 'issue_pg_net_access' + ) THEN + CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end WHEN TAG IN ('CREATE EXTENSION') + EXECUTE PROCEDURE extensions.grant_pg_net_access(); + END IF; + END + $$; + INSERT INTO supabase_functions.migrations (version) VALUES ('20210809183423_update_grants'); + ALTER function supabase_functions.http_request() SECURITY DEFINER; + ALTER function supabase_functions.http_request() SET search_path = supabase_functions; + REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC; + GRANT EXECUTE ON FUNCTION supabase_functions.http_request() TO postgres, anon, authenticated, service_role; +COMMIT; + `, + }, + { + filePath: "/volumes/functions/hello/index.ts", + content: ` +// Follow this setup guide to integrate the Deno language server with your editor: +// https://deno.land/manual/getting_started/setup_your_environment +// This enables autocomplete, go to definition, etc. + +import { serve } from "https://deno.land/std@0.177.1/http/server.ts" + +serve(async () => { + return new Response( + \`"Hello from Edge Functions!"\`, + { headers: { "Content-Type": "application/json" } }, + ) +}) + +// To invoke: +// curl 'http://localhost:/functions/v1/hello' \\ +// --header 'Authorization: Bearer ' + `, + }, + { + filePath: "/volumes/functions/main/index.ts", + content: ` +import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' +import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' + +console.log('main function started') + +const JWT_SECRET = Deno.env.get('JWT_SECRET') +const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' + +function getAuthToken(req: Request) { + const authHeader = req.headers.get('authorization') + if (!authHeader) { + throw new Error('Missing authorization header') + } + const [bearer, token] = authHeader.split(' ') + if (bearer !== 'Bearer') { + throw new Error(\`Auth header is not 'Bearer {token}'\`) + } + return token +} + +async function verifyJWT(jwt: string): Promise { + const encoder = new TextEncoder() + const secretKey = encoder.encode(JWT_SECRET) + try { + await jose.jwtVerify(jwt, secretKey) + } catch (err) { + console.error(err) + return false + } + return true +} + +serve(async (req: Request) => { + if (req.method !== 'OPTIONS' && VERIFY_JWT) { + try { + const token = getAuthToken(req) + const isValidJWT = await verifyJWT(token) + + if (!isValidJWT) { + return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } catch (e) { + console.error(e) + return new Response(JSON.stringify({ msg: e.toString() }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } + + const url = new URL(req.url) + const { pathname } = url + const path_parts = pathname.split('/') + const service_name = path_parts[1] + + if (!service_name || service_name === '') { + const error = { msg: 'missing function name in request' } + return new Response(JSON.stringify(error), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }) + } + + const servicePath = \`/home/deno/functions/\${service_name}\` + console.error(\`serving the request with \${servicePath}\`) + + const memoryLimitMb = 150 + const workerTimeoutMs = 1 * 60 * 1000 + const noModuleCache = false + const importMapPath = null + const envVarsObj = Deno.env.toObject() + const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]) + + try { + const worker = await EdgeRuntime.userWorkers.create({ + servicePath, + memoryLimitMb, + workerTimeoutMs, + noModuleCache, + importMapPath, + envVars, + }) + return await worker.fetch(req) + } catch (e) { + const error = { msg: e.toString() } + return new Response(JSON.stringify(error), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }) + } +}) + `, + }, + { + filePath: "/volumes/logs/vector.yml", + content: ` +api: + enabled: true + address: 0.0.0.0:9001 + +sources: + docker_host: + type: docker_logs + exclude_containers: + - supabase-vector + +transforms: + project_logs: + type: remap + inputs: + - docker_host + source: |- + .project = "default" + .event_message = del(.message) + .appname = del(.container_name) + del(.container_created_at) + del(.container_id) + del(.source_type) + del(.stream) + del(.label) + del(.image) + del(.host) + del(.stream) + router: + type: route + inputs: + - project_logs + route: + kong: '.appname == "supabase-kong"' + auth: '.appname == "supabase-auth"' + rest: '.appname == "supabase-rest"' + realtime: '.appname == "supabase-realtime"' + storage: '.appname == "supabase-storage"' + functions: '.appname == "supabase-functions"' + db: '.appname == "supabase-db"' + # Ignores non nginx errors since they are related with kong booting up + kong_logs: + type: remap + inputs: + - router.kong + source: |- + req, err = parse_nginx_log(.event_message, "combined") + if err == null { + .timestamp = req.timestamp + .metadata.request.headers.referer = req.referer + .metadata.request.headers.user_agent = req.agent + .metadata.request.headers.cf_connecting_ip = req.client + .metadata.request.method = req.method + .metadata.request.path = req.path + .metadata.request.protocol = req.protocol + .metadata.response.status_code = req.status + } + if err != null { + abort + } + # Ignores non nginx errors since they are related with kong booting up + kong_err: + type: remap + inputs: + - router.kong + source: |- + .metadata.request.method = "GET" + .metadata.response.status_code = 200 + parsed, err = parse_nginx_log(.event_message, "error") + if err == null { + .timestamp = parsed.timestamp + .severity = parsed.severity + .metadata.request.host = parsed.host + .metadata.request.headers.cf_connecting_ip = parsed.client + url, err = split(parsed.request, " ") + if err == null { + .metadata.request.method = url[0] + .metadata.request.path = url[1] + .metadata.request.protocol = url[2] + } + } + if err != null { + abort + } + # Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency. + auth_logs: + type: remap + inputs: + - router.auth + source: |- + parsed, err = parse_json(.event_message) + if err == null { + .metadata.timestamp = parsed.time + .metadata = merge!(.metadata, parsed) + } + # PostgREST logs are structured so we separate timestamp from message using regex + rest_logs: + type: remap + inputs: + - router.rest + source: |- + parsed, err = parse_regex(.event_message, r'^(?P