diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0b849afc0..d45c3dac0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,9 +6,9 @@ Please describe in a short paragraph what this PR is about. Before submitting this PR, please make sure that: -- [] You created a dedicated branch based on the `canary` branch. -- [] You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request -- [] You have tested this PR in your local instance. +- [ ] You created a dedicated branch based on the `canary` branch. +- [ ] You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request +- [ ] You have tested this PR in your local instance. ## Issues related (if applicable) diff --git a/apps/api/src/utils.ts b/apps/api/src/utils.ts index ee2ac3e50..0d0b574fc 100644 --- a/apps/api/src/utils.ts +++ b/apps/api/src/utils.ts @@ -1,9 +1,9 @@ import { - deployRemoteApplication, - deployRemoteCompose, - deployRemotePreviewApplication, - rebuildRemoteApplication, - rebuildRemoteCompose, + deployApplication, + deployCompose, + deployPreviewApplication, + rebuildApplication, + rebuildCompose, updateApplicationStatus, updateCompose, updatePreviewDeployment, @@ -16,13 +16,13 @@ export const deploy = async (job: DeployJob) => { await updateApplicationStatus(job.applicationId, "running"); if (job.server) { if (job.type === "redeploy") { - await rebuildRemoteApplication({ + await rebuildApplication({ applicationId: job.applicationId, titleLog: job.titleLog || "Rebuild deployment", descriptionLog: job.descriptionLog || "", }); } else if (job.type === "deploy") { - await deployRemoteApplication({ + await deployApplication({ applicationId: job.applicationId, titleLog: job.titleLog || "Manual deployment", descriptionLog: job.descriptionLog || "", @@ -36,13 +36,13 @@ export const deploy = async (job: DeployJob) => { if (job.server) { if (job.type === "redeploy") { - await rebuildRemoteCompose({ + await rebuildCompose({ composeId: job.composeId, titleLog: job.titleLog || "Rebuild deployment", descriptionLog: job.descriptionLog || "", }); } else if (job.type === "deploy") { - await deployRemoteCompose({ + await deployCompose({ composeId: job.composeId, titleLog: job.titleLog || "Manual deployment", descriptionLog: job.descriptionLog || "", @@ -55,7 +55,7 @@ export const deploy = async (job: DeployJob) => { }); if (job.server) { if (job.type === "deploy") { - await deployRemotePreviewApplication({ + await deployPreviewApplication({ applicationId: job.applicationId, titleLog: job.titleLog || "Preview Deployment", descriptionLog: job.descriptionLog || "", diff --git a/apps/dokploy/__test__/deploy/github.test.ts b/apps/dokploy/__test__/deploy/github.test.ts index 03805b08d..46be44883 100644 --- a/apps/dokploy/__test__/deploy/github.test.ts +++ b/apps/dokploy/__test__/deploy/github.test.ts @@ -1,5 +1,10 @@ import { describe, expect, it } from "vitest"; -import { extractCommitMessage } from "@/pages/api/deploy/[refreshToken]"; +import { + extractCommitMessage, + extractImageName, + extractImageTag, + extractImageTagFromRequest, +} from "@/pages/api/deploy/[refreshToken]"; describe("GitHub Webhook Skip CI", () => { const mockGithubHeaders = { @@ -96,3 +101,308 @@ describe("GitHub Webhook Skip CI", () => { ); }); }); + +describe("GitHub Packages Docker Image Tag Extraction", () => { + it("should extract tag from container_metadata", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + container_metadata: { + tag: { + name: "v1.0.0", + digest: "sha256:abc123...", + }, + }, + package_url: "ghcr.io/owner/repo:v1.0.0", + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBe("v1.0.0"); + }); + + it("should extract tag from package_url when container_metadata tag matches version", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + container_metadata: { + tag: { + name: "sha256:abc123...", + digest: "sha256:abc123...", + }, + }, + package_url: "ghcr.io/owner/repo:latest", + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBe("latest"); + }); + + it("should extract tag from package_url when container_metadata is missing", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + package_url: "ghcr.io/owner/repo:1.2.3", + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBe("1.2.3"); + }); + + it("should handle different tag formats in package_url", () => { + const headers = { "x-github-event": "registry_package" }; + const testCases = [ + { url: "ghcr.io/owner/repo:latest", expected: "latest" }, + { url: "ghcr.io/owner/repo:v1.0.0", expected: "v1.0.0" }, + { url: "ghcr.io/owner/repo:1.2.3", expected: "1.2.3" }, + { url: "ghcr.io/owner/repo:dev", expected: "dev" }, + ]; + + for (const testCase of testCases) { + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + package_url: testCase.url, + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBe(testCase.expected); + } + }); + + it("should return null for non-registry_package events", () => { + const headers = { "x-github-event": "push" }; + const body = { + registry_package: { + package_version: { + package_url: "ghcr.io/owner/repo:latest", + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBeNull(); + }); + + it("should return null when package_version is missing", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: {}, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBeNull(); + }); + + it("should return null when package_url has no tag", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + package_url: "ghcr.io/owner/repo", + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBeNull(); + }); + + it("should return null when package_url ends with colon (no tag)", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + package_url: "ghcr.io/owner/repo:", + container_metadata: { + tag: { + name: "", + digest: "sha256:abc123...", + }, + }, + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBeNull(); + }); + + it("should return null when tag name is empty string", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + container_metadata: { + tag: { + name: "", + digest: "sha256:abc123...", + }, + }, + package_url: "ghcr.io/owner/repo:", + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBeNull(); + }); + + it("should ignore tag if it matches the version (digest)", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + container_metadata: { + tag: { + name: "sha256:abc123...", + digest: "sha256:abc123...", + }, + }, + package_url: "ghcr.io/owner/repo:latest", + }, + }, + }; + + const tag = extractImageTagFromRequest(headers, body); + expect(tag).toBe("latest"); + }); + + it("should handle registry_package commit message with package_url", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + package_url: "ghcr.io/owner/repo:latest", + }, + }, + }; + + const message = extractCommitMessage(headers, body); + expect(message).toBe("Docker GHCR image pushed: ghcr.io/owner/repo:latest"); + }); + + it("should handle registry_package commit message when package_url is missing", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: { + package_version: { + version: "sha256:abc123...", + }, + }, + }; + + const message = extractCommitMessage(headers, body); + expect(message).toBe("Docker GHCR image pushed"); + }); + + it("should handle registry_package commit message when package_version is missing", () => { + const headers = { "x-github-event": "registry_package" }; + const body = { + registry_package: {}, + }; + + const message = extractCommitMessage(headers, body); + expect(message).toBe("NEW COMMIT"); + }); +}); + +describe("Docker Image Name and Tag Extraction", () => { + describe("extractImageName", () => { + it("should return image name without tag", () => { + expect(extractImageName("my-image:latest")).toBe("my-image"); + expect(extractImageName("my-image:1.0.0")).toBe("my-image"); + expect(extractImageName("ghcr.io/owner/repo:latest")).toBe( + "ghcr.io/owner/repo", + ); + }); + + it("should return full image name when no tag is present", () => { + expect(extractImageName("my-image")).toBe("my-image"); + expect(extractImageName("ghcr.io/owner/repo")).toBe("ghcr.io/owner/repo"); + }); + + it("should handle images with port numbers correctly", () => { + expect(extractImageName("registry:5000/image:tag")).toBe( + "registry:5000/image", + ); + expect(extractImageName("localhost:5000/my-app:latest")).toBe( + "localhost:5000/my-app", + ); + }); + + it("should handle complex image paths", () => { + expect( + extractImageName("myregistryhost:5000/fedora/httpd:version1.0"), + ).toBe("myregistryhost:5000/fedora/httpd"); + expect(extractImageName("registry.example.com:8080/ns/app:v1.2.3")).toBe( + "registry.example.com:8080/ns/app", + ); + }); + + it("should return null for invalid inputs", () => { + expect(extractImageName(null)).toBeNull(); + expect(extractImageName("")).toBeNull(); + }); + + it("should handle edge cases with multiple colons", () => { + expect(extractImageName("image:tag:extra")).toBe("image:tag"); + expect(extractImageName("registry:5000:invalid")).toBe("registry:5000"); + }); + }); + + describe("extractImageTag", () => { + it("should extract tag from image with tag", () => { + expect(extractImageTag("my-image:latest")).toBe("latest"); + expect(extractImageTag("my-image:1.0.0")).toBe("1.0.0"); + expect(extractImageTag("ghcr.io/owner/repo:v1.2.3")).toBe("v1.2.3"); + }); + + it("should return 'latest' when no tag is present", () => { + expect(extractImageTag("my-image")).toBe("latest"); + expect(extractImageTag("ghcr.io/owner/repo")).toBe("latest"); + }); + + it("should handle complex image paths with tags", () => { + expect( + extractImageTag("myregistryhost:5000/fedora/httpd:version1.0"), + ).toBe("version1.0"); + expect(extractImageTag("registry.example.com:8080/ns/app:v1.2.3")).toBe( + "v1.2.3", + ); + }); + + it("should return null for invalid inputs", () => { + expect(extractImageTag(null)).toBeNull(); + expect(extractImageTag("")).toBeNull(); + }); + + it("should handle edge cases with multiple colons", () => { + expect(extractImageTag("image:tag:extra")).toBe("extra"); + expect(extractImageTag("registry:5000/image:tag")).toBe("tag"); + }); + + it("should handle numeric tags", () => { + expect(extractImageTag("my-image:123")).toBe("123"); + expect(extractImageTag("my-image:1")).toBe("1"); + }); + }); +}); diff --git a/apps/dokploy/__test__/drop/drop.test.ts b/apps/dokploy/__test__/drop/drop.test.ts index cbfebbfac..112a4b25e 100644 --- a/apps/dokploy/__test__/drop/drop.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.ts @@ -42,6 +42,7 @@ const baseApp: ApplicationNested = { triggerType: "push", appName: "", autoDeploy: true, + endpointSpecSwarm: null, serverId: "", registryUrl: "", branch: null, diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 7b39eeb4f..5b48b1248 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -15,6 +15,7 @@ const baseApp: ApplicationNested = { giteaId: "", cleanCache: false, applicationStatus: "done", + endpointSpecSwarm: null, appName: "", autoDeploy: true, enableSubmodules: false, diff --git a/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx b/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx index 4227eeb44..739bd87a5 100644 --- a/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx @@ -122,6 +122,22 @@ const NetworkSwarmSchema = z.array( const LabelsSwarmSchema = z.record(z.string()); +const EndpointPortConfigSwarmSchema = z + .object({ + Protocol: z.string().optional(), + TargetPort: z.number().optional(), + PublishedPort: z.number().optional(), + PublishMode: z.string().optional(), + }) + .strict(); + +const EndpointSpecSwarmSchema = z + .object({ + Mode: z.string().optional(), + Ports: z.array(EndpointPortConfigSwarmSchema).optional(), + }) + .strict(); + const createStringToJSONSchema = (schema: z.ZodTypeAny) => { return z .string() @@ -178,6 +194,9 @@ const addSwarmSettings = z.object({ labelsSwarm: createStringToJSONSchema(LabelsSwarmSchema).nullable(), networkSwarm: createStringToJSONSchema(NetworkSwarmSchema).nullable(), stopGracePeriodSwarm: z.bigint().nullable(), + endpointSpecSwarm: createStringToJSONSchema( + EndpointSpecSwarmSchema, + ).nullable(), }); type AddSwarmSettings = z.infer; @@ -234,6 +253,7 @@ export const AddSwarmSettings = ({ id, type }: Props) => { labelsSwarm: null, networkSwarm: null, stopGracePeriodSwarm: null, + endpointSpecSwarm: null, }, resolver: zodResolver(addSwarmSettings), }); @@ -275,6 +295,9 @@ export const AddSwarmSettings = ({ id, type }: Props) => { ? JSON.stringify(data.networkSwarm, null, 2) : null, stopGracePeriodSwarm: normalizedStopGracePeriod, + endpointSpecSwarm: data.endpointSpecSwarm + ? JSON.stringify(data.endpointSpecSwarm, null, 2) + : null, }); } }, [form, form.reset, data]); @@ -296,6 +319,7 @@ export const AddSwarmSettings = ({ id, type }: Props) => { labelsSwarm: data.labelsSwarm, networkSwarm: data.networkSwarm, stopGracePeriodSwarm: data.stopGracePeriodSwarm ?? null, + endpointSpecSwarm: data.endpointSpecSwarm, }) .then(async () => { toast.success("Swarm settings updated"); @@ -846,6 +870,67 @@ export const AddSwarmSettings = ({ id, type }: Props) => { )} /> + ( + + Endpoint Spec + + + + + Check the interface + + + + + +
+														{`{
+	Mode?: string | undefined;
+	Ports?: Array<{
+		Protocol?: string | undefined;
+		TargetPort?: number | undefined;
+		PublishedPort?: number | undefined;
+		PublishMode?: string | undefined;
+	}> | undefined;
+}`}
+													
+
+
+
+
+ + + + +
+										
+									
+
+ )} + /> - - )} - + {isExpanded ? ( + <> + + Show less + + ) : ( + <> + + Show more + + )} + + )} + {/* Hash (from description) - shown in compact form */} + {deployment.description?.trim() && ( + + {deployment.description} + + )} + + +
+
+ + {deployment.startedAt && deployment.finishedAt && ( + + + {formatDuration( + Math.floor( + (new Date(deployment.finishedAt).getTime() - + new Date(deployment.startedAt).getTime()) / + 1000, + ), + )} + + )} +
- {deployment?.rollback && - deployment.status === "done" && - type === "application" && ( +
+ {deployment.pid && deployment.status === "running" && ( { - await rollback({ - rollbackId: deployment.rollback.rollbackId, + await killProcess({ + deploymentId: deployment.deploymentId, }) .then(() => { - toast.success( - "Rollback initiated successfully", - ); + toast.success("Process killed successfully"); }) .catch(() => { - toast.error("Error initiating rollback"); + toast.error("Error killing process"); }); }} > )} + + + {deployment?.rollback && + deployment.status === "done" && + type === "application" && ( + { + await rollback({ + rollbackId: deployment.rollback.rollbackId, + }) + .then(() => { + toast.success( + "Rollback initiated successfully", + ); + }) + .catch(() => { + toast.error("Error initiating rollback"); + }); + }} + > + + + )} +
- - ))} + ); + })} )} Schedule volume backups to run automatically at specified - intervals. + intervals
diff --git a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx index 6a0fb030a..01f6944e1 100644 --- a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx +++ b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx @@ -1,6 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod"; import copy from "copy-to-clipboard"; -import { debounce } from "lodash"; +import _ from "lodash"; import { CheckIcon, ChevronsUpDown, @@ -236,7 +236,7 @@ export const RestoreBackup = ({ const currentDatabaseType = form.watch("databaseType"); const metadata = form.watch("metadata"); - const debouncedSetSearch = debounce((value: string) => { + const debouncedSetSearch = _.debounce((value: string) => { setDebouncedSearchTerm(value); }, 350); diff --git a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx index 5b929f3b6..a75f50386 100644 --- a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx @@ -1,5 +1,5 @@ import { FancyAnsi } from "fancy-ansi"; -import { escapeRegExp } from "lodash"; +import _ from "lodash"; import { Badge } from "@/components/ui/badge"; import { Tooltip, @@ -47,7 +47,7 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) { } const htmlContent = fancyAnsi.toHtml(text); - const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi"); + const searchRegex = new RegExp(`(${_.escapeRegExp(term)})`, "gi"); const modifiedContent = htmlContent.replace( searchRegex, diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index 783c5bb32..5369a544e 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -14,6 +14,7 @@ import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; import { DateTooltip } from "@/components/shared/date-tooltip"; +import { FocusShortcutInput } from "@/components/shared/focus-shortcut-input"; import { StatusTooltip } from "@/components/shared/status-tooltip"; import { AlertDialog, @@ -44,7 +45,6 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { FocusShortcutInput } from "@/components/shared/focus-shortcut-input"; import { Select, SelectContent, @@ -52,12 +52,14 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { TimeBadge } from "@/components/ui/time-badge"; import { api } from "@/utils/api"; import { HandleProject } from "./handle-project"; import { ProjectEnvironment } from "./project-environment"; export const ShowProjects = () => { const utils = api.useUtils(); + const { data: isCloud } = api.settings.isCloud.useQuery(); const { data, isLoading } = api.project.all.useQuery(); const { data: auth } = api.user.get.useQuery(); const { mutateAsync } = api.project.remove.useMutation(); @@ -135,6 +137,11 @@ export const ShowProjects = () => { + {!isCloud && ( +
+ +
+ )}
@@ -148,7 +155,6 @@ export const ShowProjects = () => { Create and manage your projects - {(auth?.role === "owner" || auth?.canCreateProjects) && (
@@ -298,7 +304,13 @@ export const ShowProjects = () => { {domain.host} @@ -340,7 +352,13 @@ export const ShowProjects = () => { {domain.host} diff --git a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx index fb4d01547..7c6ef8b84 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx @@ -1,4 +1,3 @@ -import type { findEnvironmentById } from "@dokploy/server/index"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; @@ -27,12 +26,10 @@ import { FormMessage, } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; -import { api } from "@/utils/api"; +import { api, type RouterOutputs } from "@/utils/api"; -type Environment = Omit< - Awaited>, - "project" ->; +type Project = RouterOutputs["project"]["all"][number]; +type Environment = Project["environments"][number]; export type Services = { appName: string; @@ -53,17 +50,16 @@ export type Services = { }; export const extractServices = (data: Environment | undefined) => { - const applications: Services[] = - data?.applications.map((item) => ({ - appName: item.appName, - name: item.name, - type: "application", - id: item.applicationId, - createdAt: item.createdAt, - status: item.applicationStatus, - description: item.description, - serverId: item.serverId, - })) || []; + const applications: Services[] = (data?.applications?.map((item) => ({ + appName: item.appName, + name: item.name, + type: "application", + id: item.applicationId, + createdAt: item.createdAt, + status: item.applicationStatus, + description: item.description, + serverId: item.serverId, + })) ?? []) as Services[]; const mariadb: Services[] = data?.mariadb.map((item) => ({ @@ -125,17 +121,16 @@ export const extractServices = (data: Environment | undefined) => { serverId: item.serverId, })) || []; - const compose: Services[] = - data?.compose.map((item) => ({ - appName: item.appName, - name: item.name, - type: "compose", - id: item.composeId, - createdAt: item.createdAt, - status: item.composeStatus, - description: item.description, - serverId: item.serverId, - })) || []; + const compose: Services[] = (data?.compose?.map((item) => ({ + appName: item.appName, + name: item.name, + type: "compose", + id: item.composeId, + createdAt: item.createdAt, + status: item.composeStatus, + description: item.description, + serverId: item.serverId, + })) ?? []) as Services[]; applications.push( ...mysql, diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index d1d4ae273..7473fe586 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -26,6 +26,7 @@ import { PieChart, Server, ShieldCheck, + Star, Trash2, User, Users, @@ -82,6 +83,7 @@ import { AddOrganization } from "../dashboard/organization/handle-organization"; import { DialogAction } from "../shared/dialog-action"; import { Logo } from "../shared/logo"; import { Button } from "../ui/button"; +import { TimeBadge } from "../ui/time-badge"; import { UpdateServerButton } from "./update-server"; import { UserNav } from "./user-nav"; @@ -497,7 +499,6 @@ function SidebarLogo() { const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: user } = api.user.get.useQuery(); const { data: session } = authClient.useSession(); - const { data: organizations, refetch, @@ -505,6 +506,8 @@ function SidebarLogo() { } = api.organization.all.useQuery(); const { mutateAsync: deleteOrganization, isLoading: isRemoving } = api.organization.delete.useMutation(); + const { mutateAsync: setDefaultOrganization, isLoading: isSettingDefault } = + api.organization.setDefault.useMutation(); const { isMobile } = useSidebar(); const { data: activeOrganization } = authClient.useActiveOrganization(); const _utils = api.useUtils(); @@ -594,66 +597,127 @@ function SidebarLogo() { Organizations - {organizations?.map((org) => ( -
- { - await authClient.organization.setActive({ - organizationId: org.id, - }); - window.location.reload(); - }} - className="w-full gap-2 p-2" + {organizations?.map((org) => { + const isDefault = org.members?.[0]?.isDefault ?? false; + return ( +
-
{org.name}
-
- -
- - {org.ownerId === session?.user?.id && ( + { + await authClient.organization.setActive({ + organizationId: org.id, + }); + window.location.reload(); + }} + className="w-full gap-2 p-2" + > +
+
+ {org.name} +
+
+
+ +
+
+
- - { - await deleteOrganization({ + - + {isDefault ? ( + + ) : ( + + )} + + {org.ownerId === session?.user?.id && ( + <> + + { + await deleteOrganization({ + organizationId: org.id, + }) + .then(() => { + refetch(); + toast.success( + "Organization deleted successfully", + ); + }) + .catch((error) => { + toast.error( + error?.message || + "Error deleting organization", + ); + }); + }} + > + + + + )}
- )} -
- ))} +
+ ); + })} {(user?.role === "owner" || isCloud) && ( <> @@ -1062,6 +1126,7 @@ export default function Page({ children }: Props) {
+ {!isCloud && }
)} diff --git a/apps/dokploy/components/ui/dropzone.tsx b/apps/dokploy/components/ui/dropzone.tsx index 34fc270c1..b7546d73c 100644 --- a/apps/dokploy/components/ui/dropzone.tsx +++ b/apps/dokploy/components/ui/dropzone.tsx @@ -67,9 +67,10 @@ export const Dropzone = React.forwardRef( ref={inputRef} type="file" className={cn("hidden", className)} - onChange={(e: ChangeEvent) => - onChange(e.target.files) - } + onChange={(e: ChangeEvent) => { + onChange(e.target.files); + e.target.value = ""; + }} />
diff --git a/apps/dokploy/components/ui/time-badge.tsx b/apps/dokploy/components/ui/time-badge.tsx new file mode 100644 index 000000000..ea7f1f84e --- /dev/null +++ b/apps/dokploy/components/ui/time-badge.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { api } from "@/utils/api"; + +export function TimeBadge() { + const { data: serverTime } = api.server.getServerTime.useQuery(undefined); + const [time, setTime] = useState(null); + + useEffect(() => { + if (serverTime?.time) { + setTime(new Date(serverTime.time)); + } + }, [serverTime]); + + useEffect(() => { + const timer = setInterval(() => { + setTime((prevTime) => { + if (!prevTime) return null; + const newTime = new Date(prevTime.getTime() + 1000); + return newTime; + }); + }, 1000); + + return () => { + clearInterval(timer); + }; + }, []); + + if (!time || !serverTime?.timezone) { + return null; + } + + const getUtcOffset = (timeZone: string) => { + const date = new Date(); + const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" })); + const tzDate = new Date(date.toLocaleString("en-US", { timeZone })); + const offset = (tzDate.getTime() - utcDate.getTime()) / (1000 * 60 * 60); + const sign = offset >= 0 ? "+" : "-"; + const hours = Math.floor(Math.abs(offset)); + const minutes = (Math.abs(offset) * 60) % 60; + return `UTC${sign}${hours.toString().padStart(2, "0")}:${minutes + .toString() + .padStart(2, "0")}`; + }; + + return ( +
+ Server Time: + + {time.toLocaleTimeString()} + + + ({serverTime.timezone} | {getUtcOffset(serverTime.timezone)}) + +
+ ); +} diff --git a/apps/dokploy/drizzle/0119_bouncy_morbius.sql b/apps/dokploy/drizzle/0119_bouncy_morbius.sql new file mode 100644 index 000000000..5c43a339c --- /dev/null +++ b/apps/dokploy/drizzle/0119_bouncy_morbius.sql @@ -0,0 +1 @@ +ALTER TABLE "member" ADD COLUMN "is_default" boolean DEFAULT false NOT NULL; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0120_lame_captain_midlands.sql b/apps/dokploy/drizzle/0120_lame_captain_midlands.sql new file mode 100644 index 000000000..a149f7dcf --- /dev/null +++ b/apps/dokploy/drizzle/0120_lame_captain_midlands.sql @@ -0,0 +1,39 @@ +ALTER TABLE "user_temp" RENAME TO "user";--> statement-breakpoint +ALTER TABLE "user" DROP CONSTRAINT "user_temp_email_unique";--> statement-breakpoint +ALTER TABLE "account" DROP CONSTRAINT "account_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "apikey" DROP CONSTRAINT "apikey_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "invitation" DROP CONSTRAINT "invitation_inviter_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "member" DROP CONSTRAINT "member_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "organization" DROP CONSTRAINT "organization_owner_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "two_factor" DROP CONSTRAINT "two_factor_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "backup" DROP CONSTRAINT "backup_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "schedule" DROP CONSTRAINT "schedule_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "session_temp" DROP CONSTRAINT "session_temp_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "endpointSpecSwarm" json;--> statement-breakpoint +ALTER TABLE "mariadb" ADD COLUMN "endpointSpecSwarm" json;--> statement-breakpoint +ALTER TABLE "mongo" ADD COLUMN "endpointSpecSwarm" json;--> statement-breakpoint +ALTER TABLE "mysql" ADD COLUMN "endpointSpecSwarm" json;--> statement-breakpoint +ALTER TABLE "postgres" ADD COLUMN "endpointSpecSwarm" json;--> statement-breakpoint +ALTER TABLE "redis" ADD COLUMN "endpointSpecSwarm" json;--> statement-breakpoint +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "apikey" ADD CONSTRAINT "apikey_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "two_factor" ADD CONSTRAINT "two_factor_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "backup" ADD CONSTRAINT "backup_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "schedule" ADD CONSTRAINT "schedule_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "session_temp" ADD CONSTRAINT "session_temp_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user" ADD CONSTRAINT "user_email_unique" UNIQUE("email"); \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0119_snapshot.json b/apps/dokploy/drizzle/meta/0119_snapshot.json new file mode 100644 index 000000000..ba2f37232 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0119_snapshot.json @@ -0,0 +1,6686 @@ +{ + "id": "fb4a3ed9-c58b-4a79-a4ee-2a7a4d3914a7", + "prevId": "e7d24dad-8dc2-4c17-b27a-1f5240b8ffeb", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_user_id_user_temp_id_fk": { + "name": "apikey_user_id_user_temp_id_fk", + "tableFrom": "apikey", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteEnvironments": { + "name": "canDeleteEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateEnvironments": { + "name": "canCreateEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accessedEnvironments": { + "name": "accessedEnvironments", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai": { + "name": "ai", + "schema": "", + "columns": { + "aiId": { + "name": "aiId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiUrl": { + "name": "apiUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isEnabled": { + "name": "isEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ai_organizationId_organization_id_fk": { + "name": "ai_organizationId_organization_id_fk", + "tableFrom": "ai", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildSecrets": { + "name": "previewBuildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLabels": { + "name": "previewLabels", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewCustomCertResolver": { + "name": "previewCustomCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewRequireCollaboratorPermissions": { + "name": "previewRequireCollaboratorPermissions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "rollbackActive": { + "name": "rollbackActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildSecrets": { + "name": "buildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "cleanCache": { + "name": "cleanCache", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "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": "'/'" + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBuildPath": { + "name": "giteaBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "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'" + }, + "railpackVersion": { + "name": "railpackVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0.2.2'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isStaticSpa": { + "name": "isStaticSpa", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_environmentId_environment_environmentId_fk": { + "name": "application_environmentId_environment_environmentId_fk", + "tableFrom": "application", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_giteaId_gitea_giteaId_fk": { + "name": "application_giteaId_gitea_giteaId_fk", + "tableFrom": "application", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "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 + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "backupType": { + "name": "backupType", + "type": "backupType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'database'" + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "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_composeId_compose_composeId_fk": { + "name": "backup_composeId_compose_composeId_fk", + "tableFrom": "backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "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" + }, + "backup_userId_user_temp_id_fk": { + "name": "backup_userId_user_temp_id_fk", + "tableFrom": "backup", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "backup_appName_unique": { + "name": "backup_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketEmail": { + "name": "bitbucketEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "apiToken": { + "name": "apiToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeploymentsVolume": { + "name": "isolatedDeploymentsVolume", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_environmentId_environment_environmentId_fk": { + "name": "compose_environmentId_environment_environmentId_fk", + "tableFrom": "compose", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_giteaId_gitea_giteaId_fk": { + "name": "compose_giteaId_gitea_giteaId_fk", + "tableFrom": "compose", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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 + }, + "pid": { + "name": "pid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startedAt": { + "name": "startedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finishedAt": { + "name": "finishedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_scheduleId_schedule_scheduleId_fk": { + "name": "deployment_scheduleId_schedule_scheduleId_fk", + "tableFrom": "deployment", + "tableTo": "schedule", + "columnsFrom": [ + "scheduleId" + ], + "columnsTo": [ + "scheduleId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_backupId_backup_backupId_fk": { + "name": "deployment_backupId_backup_backupId_fk", + "tableFrom": "deployment", + "tableTo": "backup", + "columnsFrom": [ + "backupId" + ], + "columnsTo": [ + "backupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_rollbackId_rollback_rollbackId_fk": { + "name": "deployment_rollbackId_rollback_rollbackId_fk", + "tableFrom": "deployment", + "tableTo": "rollback", + "columnsFrom": [ + "rollbackId" + ], + "columnsTo": [ + "rollbackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_volumeBackupId_volume_backup_volumeBackupId_fk": { + "name": "deployment_volumeBackupId_volume_backup_volumeBackupId_fk", + "tableFrom": "deployment", + "tableTo": "volume_backup", + "columnsFrom": [ + "volumeBackupId" + ], + "columnsTo": [ + "volumeBackupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customCertResolver": { + "name": "customCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "internalPath": { + "name": "internalPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "stripPath": { + "name": "stripPath", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "environmentId": { + "name": "environmentId", + "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 + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "environment_projectId_project_projectId_fk": { + "name": "environment_projectId_project_projectId_fk", + "tableFrom": "environment", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitea": { + "name": "gitea", + "schema": "", + "columns": { + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "giteaUrl": { + "name": "giteaUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitea.com'" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'repo,repo:status,read:user,read:org'" + }, + "last_authenticated_at": { + "name": "last_authenticated_at", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "gitea_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitea_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitea", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_environmentId_environment_environmentId_fk": { + "name": "mariadb_environmentId_environment_environmentId_fk", + "tableFrom": "mariadb", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_environmentId_environment_environmentId_fk": { + "name": "mongo_environmentId_environment_environmentId_fk", + "tableFrom": "mongo", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_environmentId_environment_environmentId_fk": { + "name": "mysql_environmentId_environment_environmentId_fk", + "tableFrom": "mysql", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lark": { + "name": "lark", + "schema": "", + "columns": { + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_ntfyId_ntfy_ntfyId_fk": { + "name": "notification_ntfyId_ntfy_ntfyId_fk", + "tableFrom": "notification", + "tableTo": "ntfy", + "columnsFrom": [ + "ntfyId" + ], + "columnsTo": [ + "ntfyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_larkId_lark_larkId_fk": { + "name": "notification_larkId_lark_larkId_fk", + "tableFrom": "notification", + "tableTo": "lark", + "columnsFrom": [ + "larkId" + ], + "columnsTo": [ + "larkId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ntfy": { + "name": "ntfy", + "schema": "", + "columns": { + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "accessToken": { + "name": "accessToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "messageThreadId": { + "name": "messageThreadId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publishMode": { + "name": "publishMode", + "type": "publishModeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'host'" + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_environmentId_environment_environmentId_fk": { + "name": "postgres_environmentId_environment_environmentId_fk", + "tableFrom": "postgres", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_environmentId_environment_environmentId_fk": { + "name": "redis_environmentId_environment_environmentId_fk", + "tableFrom": "redis", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rollback": { + "name": "rollback", + "schema": "", + "columns": { + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fullContext": { + "name": "fullContext", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rollback_deploymentId_deployment_deploymentId_fk": { + "name": "rollback_deploymentId_deployment_deploymentId_fk", + "tableFrom": "rollback", + "tableTo": "deployment", + "columnsFrom": [ + "deploymentId" + ], + "columnsTo": [ + "deploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.schedule": { + "name": "schedule", + "schema": "", + "columns": { + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shellType": { + "name": "shellType", + "type": "shellType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'bash'" + }, + "scheduleType": { + "name": "scheduleType", + "type": "scheduleType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "script": { + "name": "script", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_applicationId_application_applicationId_fk": { + "name": "schedule_applicationId_application_applicationId_fk", + "tableFrom": "schedule", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_composeId_compose_composeId_fk": { + "name": "schedule_composeId_compose_composeId_fk", + "tableFrom": "schedule", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_serverId_server_serverId_fk": { + "name": "schedule_serverId_server_serverId_fk", + "tableFrom": "schedule", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_userId_user_temp_id_fk": { + "name": "schedule_userId_user_temp_id_fk", + "tableFrom": "schedule", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "logCleanupCron": { + "name": "logCleanupCron", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0 0 * * *'" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allowImpersonation": { + "name": "allowImpersonation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.volume_backup": { + "name": "volume_backup", + "schema": "", + "columns": { + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "turnOff": { + "name": "turnOff", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "volume_backup_applicationId_application_applicationId_fk": { + "name": "volume_backup_applicationId_application_applicationId_fk", + "tableFrom": "volume_backup", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_postgresId_postgres_postgresId_fk": { + "name": "volume_backup_postgresId_postgres_postgresId_fk", + "tableFrom": "volume_backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mariadbId_mariadb_mariadbId_fk": { + "name": "volume_backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "volume_backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mongoId_mongo_mongoId_fk": { + "name": "volume_backup_mongoId_mongo_mongoId_fk", + "tableFrom": "volume_backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mysqlId_mysql_mysqlId_fk": { + "name": "volume_backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "volume_backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_redisId_redis_redisId_fk": { + "name": "volume_backup_redisId_redis_redisId_fk", + "tableFrom": "volume_backup", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_composeId_compose_composeId_fk": { + "name": "volume_backup_composeId_compose_composeId_fk", + "tableFrom": "volume_backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_destinationId_destination_destinationId_fk": { + "name": "volume_backup_destinationId_destination_destinationId_fk", + "tableFrom": "volume_backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + "railpack" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "drop" + ] + }, + "public.backupType": { + "name": "backupType", + "schema": "public", + "values": [ + "database", + "compose" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo", + "web-server" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "raw" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error", + "cancelled" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket", + "gitea" + ] + }, + "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.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify", + "ntfy", + "lark" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.publishModeType": { + "name": "publishModeType", + "schema": "public", + "values": [ + "ingress", + "host" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.scheduleType": { + "name": "scheduleType", + "schema": "public", + "values": [ + "application", + "compose", + "server", + "dokploy-server" + ] + }, + "public.shellType": { + "name": "shellType", + "schema": "public", + "values": [ + "bash", + "sh" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none", + "custom" + ] + }, + "public.triggerType": { + "name": "triggerType", + "schema": "public", + "values": [ + "push", + "tag" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0120_snapshot.json b/apps/dokploy/drizzle/meta/0120_snapshot.json new file mode 100644 index 000000000..c566244a2 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0120_snapshot.json @@ -0,0 +1,6722 @@ +{ + "id": "bbe005b3-d5e0-4e93-8305-ba3b9a2e3f3d", + "prevId": "fb4a3ed9-c58b-4a79-a4ee-2a7a4d3914a7", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_user_id_user_id_fk": { + "name": "apikey_user_id_user_id_fk", + "tableFrom": "apikey", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteEnvironments": { + "name": "canDeleteEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateEnvironments": { + "name": "canCreateEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accessedEnvironments": { + "name": "accessedEnvironments", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_id_fk": { + "name": "organization_owner_id_user_id_fk", + "tableFrom": "organization", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_id_fk": { + "name": "two_factor_user_id_user_id_fk", + "tableFrom": "two_factor", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai": { + "name": "ai", + "schema": "", + "columns": { + "aiId": { + "name": "aiId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiUrl": { + "name": "apiUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isEnabled": { + "name": "isEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ai_organizationId_organization_id_fk": { + "name": "ai_organizationId_organization_id_fk", + "tableFrom": "ai", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildSecrets": { + "name": "previewBuildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLabels": { + "name": "previewLabels", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewCustomCertResolver": { + "name": "previewCustomCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewRequireCollaboratorPermissions": { + "name": "previewRequireCollaboratorPermissions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "rollbackActive": { + "name": "rollbackActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildSecrets": { + "name": "buildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "cleanCache": { + "name": "cleanCache", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "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": "'/'" + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBuildPath": { + "name": "giteaBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "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'" + }, + "railpackVersion": { + "name": "railpackVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0.2.2'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isStaticSpa": { + "name": "isStaticSpa", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_environmentId_environment_environmentId_fk": { + "name": "application_environmentId_environment_environmentId_fk", + "tableFrom": "application", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_giteaId_gitea_giteaId_fk": { + "name": "application_giteaId_gitea_giteaId_fk", + "tableFrom": "application", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "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 + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "backupType": { + "name": "backupType", + "type": "backupType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'database'" + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "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_composeId_compose_composeId_fk": { + "name": "backup_composeId_compose_composeId_fk", + "tableFrom": "backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "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" + }, + "backup_userId_user_id_fk": { + "name": "backup_userId_user_id_fk", + "tableFrom": "backup", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "backup_appName_unique": { + "name": "backup_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketEmail": { + "name": "bitbucketEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "apiToken": { + "name": "apiToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeploymentsVolume": { + "name": "isolatedDeploymentsVolume", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_environmentId_environment_environmentId_fk": { + "name": "compose_environmentId_environment_environmentId_fk", + "tableFrom": "compose", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_giteaId_gitea_giteaId_fk": { + "name": "compose_giteaId_gitea_giteaId_fk", + "tableFrom": "compose", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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 + }, + "pid": { + "name": "pid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startedAt": { + "name": "startedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finishedAt": { + "name": "finishedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_scheduleId_schedule_scheduleId_fk": { + "name": "deployment_scheduleId_schedule_scheduleId_fk", + "tableFrom": "deployment", + "tableTo": "schedule", + "columnsFrom": [ + "scheduleId" + ], + "columnsTo": [ + "scheduleId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_backupId_backup_backupId_fk": { + "name": "deployment_backupId_backup_backupId_fk", + "tableFrom": "deployment", + "tableTo": "backup", + "columnsFrom": [ + "backupId" + ], + "columnsTo": [ + "backupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_rollbackId_rollback_rollbackId_fk": { + "name": "deployment_rollbackId_rollback_rollbackId_fk", + "tableFrom": "deployment", + "tableTo": "rollback", + "columnsFrom": [ + "rollbackId" + ], + "columnsTo": [ + "rollbackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_volumeBackupId_volume_backup_volumeBackupId_fk": { + "name": "deployment_volumeBackupId_volume_backup_volumeBackupId_fk", + "tableFrom": "deployment", + "tableTo": "volume_backup", + "columnsFrom": [ + "volumeBackupId" + ], + "columnsTo": [ + "volumeBackupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customCertResolver": { + "name": "customCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "internalPath": { + "name": "internalPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "stripPath": { + "name": "stripPath", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "environmentId": { + "name": "environmentId", + "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 + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "environment_projectId_project_projectId_fk": { + "name": "environment_projectId_project_projectId_fk", + "tableFrom": "environment", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "git_provider_userId_user_id_fk": { + "name": "git_provider_userId_user_id_fk", + "tableFrom": "git_provider", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitea": { + "name": "gitea", + "schema": "", + "columns": { + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "giteaUrl": { + "name": "giteaUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitea.com'" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'repo,repo:status,read:user,read:org'" + }, + "last_authenticated_at": { + "name": "last_authenticated_at", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "gitea_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitea_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitea", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_environmentId_environment_environmentId_fk": { + "name": "mariadb_environmentId_environment_environmentId_fk", + "tableFrom": "mariadb", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_environmentId_environment_environmentId_fk": { + "name": "mongo_environmentId_environment_environmentId_fk", + "tableFrom": "mongo", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_environmentId_environment_environmentId_fk": { + "name": "mysql_environmentId_environment_environmentId_fk", + "tableFrom": "mysql", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lark": { + "name": "lark", + "schema": "", + "columns": { + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_ntfyId_ntfy_ntfyId_fk": { + "name": "notification_ntfyId_ntfy_ntfyId_fk", + "tableFrom": "notification", + "tableTo": "ntfy", + "columnsFrom": [ + "ntfyId" + ], + "columnsTo": [ + "ntfyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_larkId_lark_larkId_fk": { + "name": "notification_larkId_lark_larkId_fk", + "tableFrom": "notification", + "tableTo": "lark", + "columnsFrom": [ + "larkId" + ], + "columnsTo": [ + "larkId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ntfy": { + "name": "ntfy", + "schema": "", + "columns": { + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "accessToken": { + "name": "accessToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "messageThreadId": { + "name": "messageThreadId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publishMode": { + "name": "publishMode", + "type": "publishModeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'host'" + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_environmentId_environment_environmentId_fk": { + "name": "postgres_environmentId_environment_environmentId_fk", + "tableFrom": "postgres", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "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 + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_environmentId_environment_environmentId_fk": { + "name": "redis_environmentId_environment_environmentId_fk", + "tableFrom": "redis", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rollback": { + "name": "rollback", + "schema": "", + "columns": { + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fullContext": { + "name": "fullContext", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rollback_deploymentId_deployment_deploymentId_fk": { + "name": "rollback_deploymentId_deployment_deploymentId_fk", + "tableFrom": "rollback", + "tableTo": "deployment", + "columnsFrom": [ + "deploymentId" + ], + "columnsTo": [ + "deploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.schedule": { + "name": "schedule", + "schema": "", + "columns": { + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shellType": { + "name": "shellType", + "type": "shellType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'bash'" + }, + "scheduleType": { + "name": "scheduleType", + "type": "scheduleType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "script": { + "name": "script", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_applicationId_application_applicationId_fk": { + "name": "schedule_applicationId_application_applicationId_fk", + "tableFrom": "schedule", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_composeId_compose_composeId_fk": { + "name": "schedule_composeId_compose_composeId_fk", + "tableFrom": "schedule", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_serverId_server_serverId_fk": { + "name": "schedule_serverId_server_serverId_fk", + "tableFrom": "schedule", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_userId_user_id_fk": { + "name": "schedule_userId_user_id_fk", + "tableFrom": "schedule", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_id_fk": { + "name": "session_temp_user_id_user_id_fk", + "tableFrom": "session_temp", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "logCleanupCron": { + "name": "logCleanupCron", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0 0 * * *'" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allowImpersonation": { + "name": "allowImpersonation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.volume_backup": { + "name": "volume_backup", + "schema": "", + "columns": { + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "turnOff": { + "name": "turnOff", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "volume_backup_applicationId_application_applicationId_fk": { + "name": "volume_backup_applicationId_application_applicationId_fk", + "tableFrom": "volume_backup", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_postgresId_postgres_postgresId_fk": { + "name": "volume_backup_postgresId_postgres_postgresId_fk", + "tableFrom": "volume_backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mariadbId_mariadb_mariadbId_fk": { + "name": "volume_backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "volume_backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mongoId_mongo_mongoId_fk": { + "name": "volume_backup_mongoId_mongo_mongoId_fk", + "tableFrom": "volume_backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mysqlId_mysql_mysqlId_fk": { + "name": "volume_backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "volume_backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_redisId_redis_redisId_fk": { + "name": "volume_backup_redisId_redis_redisId_fk", + "tableFrom": "volume_backup", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_composeId_compose_composeId_fk": { + "name": "volume_backup_composeId_compose_composeId_fk", + "tableFrom": "volume_backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_destinationId_destination_destinationId_fk": { + "name": "volume_backup_destinationId_destination_destinationId_fk", + "tableFrom": "volume_backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + "railpack" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "drop" + ] + }, + "public.backupType": { + "name": "backupType", + "schema": "public", + "values": [ + "database", + "compose" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo", + "web-server" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "raw" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error", + "cancelled" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket", + "gitea" + ] + }, + "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.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify", + "ntfy", + "lark" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.publishModeType": { + "name": "publishModeType", + "schema": "public", + "values": [ + "ingress", + "host" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.scheduleType": { + "name": "scheduleType", + "schema": "public", + "values": [ + "application", + "compose", + "server", + "dokploy-server" + ] + }, + "public.shellType": { + "name": "shellType", + "schema": "public", + "values": [ + "bash", + "sh" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none", + "custom" + ] + }, + "public.triggerType": { + "name": "triggerType", + "schema": "public", + "values": [ + "push", + "tag" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 9d6de3b90..c51c28816 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -834,6 +834,20 @@ "when": 1761415824484, "tag": "0118_loose_anita_blake", "breakpoints": true + }, + { + "idx": 119, + "version": "7", + "when": 1762142756443, + "tag": "0119_bouncy_morbius", + "breakpoints": true + }, + { + "idx": 120, + "version": "7", + "when": 1762632540024, + "tag": "0120_lame_captain_midlands", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 183771f79..1b33df019 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.25.6", + "version": "v0.25.7", "private": true, "license": "Apache-2.0", "type": "module", @@ -120,7 +120,7 @@ "next": "^15.3.2", "next-i18next": "^15.4.2", "next-themes": "^0.2.1", - "node-os-utils": "1.3.7", + "node-os-utils": "2.0.1", "node-pty": "1.0.0", "node-schedule": "2.1.1", "nodemailer": "6.9.14", @@ -163,7 +163,6 @@ "@types/lodash": "4.17.4", "@types/micromatch": "4.0.9", "@types/node": "^18.19.104", - "@types/node-os-utils": "1.3.4", "@types/node-schedule": "2.1.6", "@types/nodemailer": "^6.4.17", "@types/qrcode": "^1.5.5", diff --git a/apps/dokploy/pages/api/deploy/[refreshToken].ts b/apps/dokploy/pages/api/deploy/[refreshToken].ts index fd01c81fa..2ab607736 100644 --- a/apps/dokploy/pages/api/deploy/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/[refreshToken].ts @@ -12,6 +12,17 @@ import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; import { deploy } from "@/server/utils/deploy"; +/** + * Helper function to get package_version from registry_package events + */ +const getPackageVersion = (headers: any, body: any) => { + const event = headers["x-github-event"]; + if (event === "registry_package") { + return body.registry_package?.package_version; + } + return null; +}; + export default async function handler( req: NextApiRequest, res: NextApiResponse, @@ -46,21 +57,60 @@ export default async function handler( } const deploymentTitle = extractCommitMessage(req.headers, req.body); - const deploymentHash = extractHash(req.headers, req.body); + const deploymentHash = extractHash(req.headers, req.body); const sourceType = application.sourceType; if (sourceType === "docker") { + const applicationImageName = extractImageName(application.dockerImage); const applicationDockerTag = extractImageTag(application.dockerImage); + + const webhookImageName = extractImageNameFromRequest( + req.headers, + req.body, + ); const webhookDockerTag = extractImageTagFromRequest( req.headers, req.body, ); - if ( - applicationDockerTag && - webhookDockerTag && - webhookDockerTag !== applicationDockerTag - ) { + + if (!applicationImageName) { + res.status(301).json({ + message: "Application Docker Image Name Not Found", + }); + return; + } + + if (!webhookImageName) { + res.status(301).json({ + message: "Webhook Docker Image Name Not Found", + }); + return; + } + + // Validate image name matches + if (webhookImageName !== applicationImageName) { + res.status(301).json({ + message: `Application Image Name (${applicationImageName}) doesn't match request event payload Image Name (${webhookImageName}).`, + }); + return; + } + + if (!applicationDockerTag) { + res.status(301).json({ + message: "Application Docker Tag Not Found", + }); + return; + } + + if (!webhookDockerTag) { + res.status(301).json({ + message: "Webhook Docker Tag Not Found", + }); + return; + } + + if (webhookDockerTag !== applicationDockerTag) { res.status(301).json({ message: `Application Image Tag (${applicationDockerTag}) doesn't match request event payload Image Tag (${webhookDockerTag}).`, }); @@ -191,7 +241,7 @@ export default async function handler( const jobData: DeploymentJob = { applicationId: application.applicationId as string, titleLog: deploymentTitle, - descriptionLog: `Hash: ${deploymentHash}`, + ...(deploymentHash && { descriptionLog: `Hash: ${deploymentHash}` }), type: "deploy", applicationType: "application", server: !!application.serverId, @@ -222,6 +272,39 @@ export default async function handler( } } +/** + * Return the image name without the tag + * Example: "my-image" => "my-image" + * Example: "my-image:latest" => "my-image" + * Example: "my-image:1.0.0" => "my-image" + * Example: "myregistryhost:5000/fedora/httpd:version1.0" => "myregistryhost:5000/fedora/httpd" + * @link https://docs.docker.com/reference/cli/docker/image/tag/ + */ +export function extractImageName(dockerImage: string | null): string | null { + if (!dockerImage || typeof dockerImage !== "string") { + return null; + } + + // Handle case where there's no tag (no colon or colon is part of port number) + const lastColonIndex = dockerImage.lastIndexOf(":"); + if (lastColonIndex === -1) { + return dockerImage; + } + + // Check if the part after the last colon looks like a tag (not a port number) + // Port numbers are typically 1-5 digits, tags are usually longer or contain letters + const afterColon = dockerImage.substring(lastColonIndex + 1); + const isPortNumber = /^\d{1,5}$/.test(afterColon); + + // If it's a port number (like registry:5000/image), don't split + if (isPortNumber) { + return dockerImage; + } + + // Otherwise, split at the last colon to get image name + return dockerImage.substring(0, lastColonIndex); +} + /** * Return the last part of the image name, which is the tag * Example: "my-image" => null @@ -230,7 +313,7 @@ export default async function handler( * Example: "myregistryhost:5000/fedora/httpd:version1.0" => "version1.0" * @link https://docs.docker.com/reference/cli/docker/image/tag/ */ -function extractImageTag(dockerImage: string | null) { +export function extractImageTag(dockerImage: string | null) { if (!dockerImage || typeof dockerImage !== "string") { return null; } @@ -240,12 +323,78 @@ function extractImageTag(dockerImage: string | null) { } /** + * Extract the image name (without tag) from webhook request * @link https://docs.docker.com/docker-hub/webhooks/#example-webhook-payload + * @link https://docs.github.com/en/webhooks/webhook-events-and-payloads#registry_package + */ +export const extractImageNameFromRequest = ( + headers: any, + body: any, +): string | null => { + // GitHub Packages: registry_package events (container registry) + const packageVersion = getPackageVersion(headers, body); + if (packageVersion?.package_url) { + const packageUrl = packageVersion.package_url; + // Remove tag if present (everything after the last colon) + if (packageUrl.includes(":")) { + const lastColonIndex = packageUrl.lastIndexOf(":"); + // Check if it's a port number (like registry:5000/image) + const afterColon = packageUrl.substring(lastColonIndex + 1); + const isPortNumber = /^\d{1,5}$/.test(afterColon); + if (isPortNumber) { + return packageUrl; + } + return packageUrl.substring(0, lastColonIndex); + } + return packageUrl; + } + + // Docker Hub + if (headers["user-agent"]?.includes("Go-http-client")) { + if (body.repository) { + const repoName = body.repository.repo_name; + return `${repoName}`; + } + } + return null; +}; + +/** + * @link https://docs.docker.com/docker-hub/webhooks/#example-webhook-payload + * @link https://docs.github.com/en/webhooks/webhook-events-and-payloads#registry_package */ export const extractImageTagFromRequest = ( headers: any, body: any, ): string | null => { + // GitHub Packages: registry_package events (container registry) + const packageVersion = getPackageVersion(headers, body); + if (packageVersion) { + // Try to get tag from container_metadata first (most reliable) + // Only use it if it's not empty and not the same as the version (digest) + const tagName = packageVersion.container_metadata?.tag?.name?.trim() || ""; + if ( + tagName && + tagName !== packageVersion.version && + !tagName.startsWith("sha256:") + ) { + return tagName; + } + // Fallback: extract tag from package_url (e.g., "ghcr.io/owner/repo:tag") + if (packageVersion.package_url) { + const packageUrl = packageVersion.package_url; + // Handle case where package_url ends with colon (no tag) + if (packageUrl.endsWith(":")) { + return null; + } + const tagMatch = packageUrl.match(/:([^:]+)$/); + if (tagMatch?.[1]?.trim()) { + return tagMatch[1].trim(); + } + } + } + + // Docker Hub if (headers["user-agent"]?.includes("Go-http-client")) { if (body.push_data && body.repository) { return body.push_data.tag; @@ -255,6 +404,18 @@ export const extractImageTagFromRequest = ( }; export const extractCommitMessage = (headers: any, body: any) => { + // GitHub Packages: registry_package events (container tags) + const githubEvent = headers["x-github-event"]; + if (githubEvent === "registry_package") { + const packageVersion = getPackageVersion(headers, body); + if (packageVersion) { + if (packageVersion.package_url) { + return `Docker GHCR image pushed: ${packageVersion.package_url}`; + } + return "Docker GHCR image pushed"; + } + // If package_version is missing, fall through to default behavior + } // GitHub if (headers["x-github-event"]) { return body.head_commit ? body.head_commit.message : "NEW COMMIT"; @@ -283,7 +444,7 @@ export const extractCommitMessage = (headers: any, body: any) => { if (headers["user-agent"]?.includes("Go-http-client")) { if (body.push_data && body.repository) { - return `Docker image pushed: ${body.repository.repo_name}:${body.push_data.tag} by ${body.push_data.pusher}`; + return `DockerHub image pushed: ${body.repository.repo_name}:${body.push_data.tag} by ${body.push_data.pusher}`; } } diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index ec6265e89..b44b63d45 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -4,7 +4,7 @@ import { asc, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import Stripe from "stripe"; import { db } from "@/server/db"; -import { organization, server, users_temp } from "@/server/db/schema"; +import { organization, server, user } from "@/server/db/schema"; const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET!; @@ -64,13 +64,13 @@ export default async function handler( session.subscription as string, ); await db - .update(users_temp) + .update(user) .set({ stripeCustomerId: session.customer as string, stripeSubscriptionId: session.subscription as string, serversQuantity: subscription?.items?.data?.[0]?.quantity ?? 0, }) - .where(eq(users_temp.id, adminId)) + .where(eq(user.id, adminId)) .returning(); const admin = await findUserById(adminId); @@ -85,14 +85,12 @@ export default async function handler( const newSubscription = event.data.object as Stripe.Subscription; await db - .update(users_temp) + .update(user) .set({ stripeSubscriptionId: newSubscription.id, stripeCustomerId: newSubscription.customer as string, }) - .where( - eq(users_temp.stripeCustomerId, newSubscription.customer as string), - ) + .where(eq(user.stripeCustomerId, newSubscription.customer as string)) .returning(); break; @@ -102,14 +100,12 @@ export default async function handler( const newSubscription = event.data.object as Stripe.Subscription; await db - .update(users_temp) + .update(user) .set({ stripeSubscriptionId: null, serversQuantity: 0, }) - .where( - eq(users_temp.stripeCustomerId, newSubscription.customer as string), - ); + .where(eq(user.stripeCustomerId, newSubscription.customer as string)); const admin = await findUserByStripeCustomerId( newSubscription.customer as string, @@ -135,24 +131,20 @@ export default async function handler( if (newSubscription.status === "active") { await db - .update(users_temp) + .update(user) .set({ serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0, }) - .where( - eq(users_temp.stripeCustomerId, newSubscription.customer as string), - ); + .where(eq(user.stripeCustomerId, newSubscription.customer as string)); const newServersQuantity = admin.serversQuantity; await updateServersBasedOnQuantity(admin.id, newServersQuantity); } else { await disableServers(admin.id); await db - .update(users_temp) + .update(user) .set({ serversQuantity: 0 }) - .where( - eq(users_temp.stripeCustomerId, newSubscription.customer as string), - ); + .where(eq(user.stripeCustomerId, newSubscription.customer as string)); } break; @@ -172,11 +164,11 @@ export default async function handler( } await db - .update(users_temp) + .update(user) .set({ serversQuantity: suscription?.items?.data?.[0]?.quantity ?? 0, }) - .where(eq(users_temp.stripeCustomerId, suscription.customer as string)); + .where(eq(user.stripeCustomerId, suscription.customer as string)); const admin = await findUserByStripeCustomerId( suscription.customer as string, @@ -205,13 +197,11 @@ export default async function handler( return res.status(400).send("Webhook Error: Admin not found"); } await db - .update(users_temp) + .update(user) .set({ serversQuantity: 0, }) - .where( - eq(users_temp.stripeCustomerId, newInvoice.customer as string), - ); + .where(eq(user.stripeCustomerId, newInvoice.customer as string)); await disableServers(admin.id); } @@ -229,13 +219,13 @@ export default async function handler( await disableServers(admin.id); await db - .update(users_temp) + .update(user) .set({ stripeCustomerId: null, stripeSubscriptionId: null, serversQuantity: 0, }) - .where(eq(users_temp.stripeCustomerId, customer.id)); + .where(eq(user.stripeCustomerId, customer.id)); break; } @@ -262,10 +252,10 @@ const disableServers = async (userId: string) => { }; const findUserByStripeCustomerId = async (stripeCustomerId: string) => { - const user = db.query.users_temp.findFirst({ - where: eq(users_temp.stripeCustomerId, stripeCustomerId), + const userResult = await db.query.user.findFirst({ + where: eq(user.stripeCustomerId, stripeCustomerId), }); - return user; + return userResult; }; const activateServer = async (serverId: string) => { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx index c09111d20..a2e54ad51 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx @@ -1,4 +1,4 @@ -import type { findProjectById } from "@dokploy/server"; +import type { findEnvironmentById } from "@dokploy/server"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { @@ -102,6 +102,7 @@ import { api } from "@/utils/api"; export type Services = { appName: string; serverId?: string | null; + serverName?: string | null; name: string; type: | "mariadb" @@ -115,10 +116,10 @@ export type Services = { id: string; createdAt: string; status?: "idle" | "running" | "done" | "error"; + lastDeployDate?: Date | null; }; -type Project = Awaited>; -type Environment = Project["environments"][0]; +type Environment = Awaited>; export const extractServicesFromEnvironment = ( environment: Environment | undefined, @@ -128,16 +129,35 @@ export const extractServicesFromEnvironment = ( const allServices: Services[] = []; const applications: Services[] = - environment.applications?.map((item) => ({ - appName: item.appName, - name: item.name, - type: "application", - id: item.applicationId, - createdAt: item.createdAt, - status: item.applicationStatus, - description: item.description, - serverId: item.serverId, - })) || []; + environment.applications?.map((item) => { + // Get the most recent deployment date + let lastDeployDate: Date | null = null; + const deployments = (item as any).deployments; + if (deployments && deployments.length > 0) { + for (const deployment of deployments) { + const deployDate = new Date( + deployment.finishedAt || + deployment.startedAt || + deployment.createdAt, + ); + if (!lastDeployDate || deployDate > lastDeployDate) { + lastDeployDate = deployDate; + } + } + } + return { + appName: item.appName, + name: item.name, + type: "application", + id: item.applicationId, + createdAt: item.createdAt, + status: item.applicationStatus, + description: item.description, + serverId: item.serverId, + serverName: item?.server?.name || null, + lastDeployDate, + }; + }) || []; const mariadb: Services[] = environment.mariadb?.map((item) => ({ @@ -149,6 +169,7 @@ export const extractServicesFromEnvironment = ( status: item.applicationStatus, description: item.description, serverId: item.serverId, + serverName: item?.server?.name || null, })) || []; const postgres: Services[] = @@ -161,6 +182,7 @@ export const extractServicesFromEnvironment = ( status: item.applicationStatus, description: item.description, serverId: item.serverId, + serverName: item?.server?.name || null, })) || []; const mongo: Services[] = @@ -173,6 +195,7 @@ export const extractServicesFromEnvironment = ( status: item.applicationStatus, description: item.description, serverId: item.serverId, + serverName: item?.server?.name || null, })) || []; const redis: Services[] = @@ -185,6 +208,7 @@ export const extractServicesFromEnvironment = ( status: item.applicationStatus, description: item.description, serverId: item.serverId, + serverName: item?.server?.name || null, })) || []; const mysql: Services[] = @@ -197,19 +221,39 @@ export const extractServicesFromEnvironment = ( status: item.applicationStatus, description: item.description, serverId: item.serverId, + serverName: item?.server?.name || null, })) || []; const compose: Services[] = - environment.compose?.map((item) => ({ - appName: item.appName, - name: item.name, - type: "compose", - id: item.composeId, - createdAt: item.createdAt, - status: item.composeStatus, - description: item.description, - serverId: item.serverId, - })) || []; + environment.compose?.map((item) => { + // Get the most recent deployment date + let lastDeployDate: Date | null = null; + const deployments = (item as any).deployments; + if (deployments && deployments.length > 0) { + for (const deployment of deployments) { + const deployDate = new Date( + deployment.finishedAt || + deployment.startedAt || + deployment.createdAt, + ); + if (!lastDeployDate || deployDate > lastDeployDate) { + lastDeployDate = deployDate; + } + } + } + return { + appName: item.appName, + name: item.name, + type: "compose", + id: item.composeId, + createdAt: item.createdAt, + status: item.composeStatus, + description: item.description, + serverId: item.serverId, + serverName: item?.server?.name || null, + lastDeployDate, + }; + }) || []; allServices.push( ...applications, @@ -237,9 +281,9 @@ const EnvironmentPage = ( const { data: auth } = api.user.get.useQuery(); const [sortBy, setSortBy] = useState(() => { if (typeof window !== "undefined") { - return localStorage.getItem("servicesSort") || "createdAt-desc"; + return localStorage.getItem("servicesSort") || "lastDeploy-desc"; } - return "createdAt-desc"; + return "lastDeploy-desc"; }); useEffect(() => { @@ -261,10 +305,45 @@ const EnvironmentPage = ( comparison = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); break; + case "lastDeploy": { + const aLastDeploy = a.lastDeployDate; + const bLastDeploy = b.lastDeployDate; + + if (direction === "desc") { + // For "desc" (newest first): services with deployments first, then those without + if (!aLastDeploy && !bLastDeploy) { + comparison = 0; + } else if (!aLastDeploy) { + comparison = 1; // a (no deploy) goes after b (has deploy) + } else if (!bLastDeploy) { + comparison = -1; // a (has deploy) goes before b (no deploy) + } else { + // Both have deployments: newest first (negative if a is newer) + comparison = bLastDeploy.getTime() - aLastDeploy.getTime(); + } + } else { + // For "asc" (oldest first): services with deployments first, then those without + if (!aLastDeploy && !bLastDeploy) { + comparison = 0; + } else if (!aLastDeploy) { + comparison = 1; // a (no deploy) goes after b (has deploy) + } else if (!bLastDeploy) { + comparison = -1; // a (has deploy) goes before b (no deploy) + } else { + // Both have deployments: oldest first + comparison = aLastDeploy.getTime() - bLastDeploy.getTime(); + } + } + break; + } default: comparison = 0; } - return direction === "asc" ? comparison : -comparison; + // For other fields, apply direction normally + if (field !== "lastDeploy") { + return direction === "asc" ? comparison : -comparison; + } + return comparison; }); }; @@ -320,6 +399,7 @@ const EnvironmentPage = ( const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false); const [deleteVolumes, setDeleteVolumes] = useState(false); + const [selectedServerId, setSelectedServerId] = useState("all"); const handleSelectAll = () => { if (selectedServices.length === filteredServices.length) { @@ -709,6 +789,27 @@ const EnvironmentPage = ( setIsBulkActionLoading(false); }; + // Get unique servers from services + const availableServers = useMemo(() => { + if (!applications) return []; + const servers = new Map(); + applications.forEach((service) => { + if (service.serverId && service.serverName) { + servers.set(service.serverId, { + serverId: service.serverId, + serverName: service.serverName, + }); + } + }); + return Array.from(servers.values()); + }, [applications]); + + // Check if there are services without a server (Dokploy server) + const hasServicesWithoutServer = useMemo(() => { + if (!applications) return false; + return applications.some((service) => !service.serverId); + }, [applications]); + const filteredServices = useMemo(() => { if (!applications) return []; const filtered = applications.filter( @@ -717,10 +818,14 @@ const EnvironmentPage = ( service.description ?.toLowerCase() .includes(searchQuery.toLowerCase())) && - (selectedTypes.length === 0 || selectedTypes.includes(service.type)), + (selectedTypes.length === 0 || selectedTypes.includes(service.type)) && + (selectedServerId === "" || + selectedServerId === "all" || + (selectedServerId === "dokploy-server" && !service.serverId) || + service.serverId === selectedServerId), ); return sortServices(filtered); - }, [applications, searchQuery, selectedTypes, sortBy]); + }, [applications, searchQuery, selectedTypes, selectedServerId, sortBy]); const selectedServicesWithRunningStatus = useMemo(() => { return filteredServices.filter( @@ -1217,6 +1322,9 @@ const EnvironmentPage = ( + + Recently deployed + Newest first @@ -1291,6 +1399,39 @@ const EnvironmentPage = ( + {(availableServers.length > 0 || + hasServicesWithoutServer) && ( + + )}
@@ -1396,7 +1537,15 @@ const EnvironmentPage = ( -
+
+ {service.serverName && ( +
+ + + {service.serverName} + +
+ )} Created diff --git a/apps/dokploy/reset-2fa.ts b/apps/dokploy/reset-2fa.ts index 089b7118b..77af3d506 100644 --- a/apps/dokploy/reset-2fa.ts +++ b/apps/dokploy/reset-2fa.ts @@ -1,6 +1,6 @@ import { findAdmin } from "@dokploy/server"; import { db } from "@dokploy/server/db"; -import { users_temp } from "@dokploy/server/db/schema"; +import { user } from "@dokploy/server/db/schema"; import { eq } from "drizzle-orm"; (async () => { @@ -8,11 +8,11 @@ import { eq } from "drizzle-orm"; const result = await findAdmin(); const update = await db - .update(users_temp) + .update(user) .set({ twoFactorEnabled: false, }) - .where(eq(users_temp.id, result.userId)); + .where(eq(user.id, result.userId)); if (update) { console.log("2FA reset successful"); diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 512ea9718..026b6e8ad 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -3,13 +3,14 @@ import { addNewService, checkServiceAccess, cloneCompose, - cloneComposeRemote, createCommand, createCompose, createComposeByTemplate, createDomain, createMount, deleteMount, + execAsync, + execAsyncRemote, findComposeById, findDomainsByComposeId, findEnvironmentById, @@ -245,6 +246,7 @@ export const composeRouter = createTRPCRouter({ }); } await cleanQueuesByCompose(input.composeId); + return { success: true, message: "Queues cleaned successfully" }; }), loadServices: protectedProcedure @@ -301,10 +303,12 @@ export const composeRouter = createTRPCRouter({ message: "You are not authorized to fetch this compose", }); } + + const command = await cloneCompose(compose); if (compose.serverId) { - await cloneComposeRemote(compose); + await execAsyncRemote(compose.serverId, command); } else { - await cloneCompose(compose); + await execAsync(command); } return compose.sourceType; } catch (err) { @@ -405,6 +409,7 @@ export const composeRouter = createTRPCRouter({ removeOnFail: true, }, ); + return { success: true, message: "Deployment queued" }; }), redeploy: protectedProcedure .input(apiRedeployCompose) @@ -440,6 +445,7 @@ export const composeRouter = createTRPCRouter({ removeOnFail: true, }, ); + return { success: true, message: "Redeployment queued" }; }), stop: protectedProcedure .input(apiFindCompose) diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index ca24926bf..56748dc98 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -1,8 +1,8 @@ import { createDiscordNotification, createEmailNotification, - createLarkNotification, createGotifyNotification, + createLarkNotification, createNtfyNotification, createSlackNotification, createTelegramNotification, @@ -11,16 +11,16 @@ import { removeNotificationById, sendDiscordNotification, sendEmailNotification, - sendLarkNotification, sendGotifyNotification, + sendLarkNotification, sendNtfyNotification, sendServerThresholdNotifications, sendSlackNotification, sendTelegramNotification, updateDiscordNotification, updateEmailNotification, - updateLarkNotification, updateGotifyNotification, + updateLarkNotification, updateNtfyNotification, updateSlackNotification, updateTelegramNotification, @@ -38,29 +38,29 @@ import { db } from "@/server/db"; import { apiCreateDiscord, apiCreateEmail, - apiCreateLark, apiCreateGotify, + apiCreateLark, apiCreateNtfy, apiCreateSlack, apiCreateTelegram, apiFindOneNotification, apiTestDiscordConnection, apiTestEmailConnection, - apiTestLarkConnection, apiTestGotifyConnection, + apiTestLarkConnection, apiTestNtfyConnection, apiTestSlackConnection, apiTestTelegramConnection, apiUpdateDiscord, apiUpdateEmail, - apiUpdateLark, apiUpdateGotify, + apiUpdateLark, apiUpdateNtfy, apiUpdateSlack, apiUpdateTelegram, notifications, server, - users_temp, + user, } from "@/server/db/schema"; export const notificationRouter = createTRPCRouter({ @@ -359,9 +359,9 @@ export const notificationRouter = createTRPCRouter({ if (input.ServerType === "Dokploy") { const result = await db .select() - .from(users_temp) + .from(user) .where( - sql`${users_temp.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`, + sql`${user.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`, ); if (!result?.[0]?.id) { diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index a015310d1..e8ca9eb16 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -41,6 +41,11 @@ export const organizationRouter = createTRPCRouter({ }); } + // Check if this is the user's first organization + const existingMemberships = await db.query.member.findMany({ + where: eq(member.userId, ctx.user.id), + }); + await db.insert(member).values({ organizationId: result.id, role: "owner", @@ -63,6 +68,11 @@ export const organizationRouter = createTRPCRouter({ ), ), ), + with: { + members: { + where: eq(member.userId, ctx.user.id), + }, + }, }); return memberResult; }), @@ -184,4 +194,45 @@ export const organizationRouter = createTRPCRouter({ .delete(invitation) .where(eq(invitation.id, input.invitationId)); }), + setDefault: protectedProcedure + .input( + z.object({ + organizationId: z.string().min(1), + }), + ) + .mutation(async ({ ctx, input }) => { + // Verify user is a member of this organization + const userMember = await db.query.member.findFirst({ + where: and( + eq(member.organizationId, input.organizationId), + eq(member.userId, ctx.user.id), + ), + }); + + if (!userMember) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "You are not a member of this organization", + }); + } + + // First, unset all defaults for this user + await db + .update(member) + .set({ isDefault: false }) + .where(eq(member.userId, ctx.user.id)); + + // Then set this organization as default + await db + .update(member) + .set({ isDefault: true }) + .where( + and( + eq(member.organizationId, input.organizationId), + eq(member.userId, ctx.user.id), + ), + ); + + return { success: true }; + }), }); diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index d6904a7ec..8a01228f8 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -383,6 +383,15 @@ export const serverRouter = createTRPCRouter({ const ip = await getPublicIpWithFallback(); return ip; }), + getServerTime: protectedProcedure.query(() => { + if (IS_CLOUD) { + return null; + } + return { + time: new Date(), + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + }; + }), getServerMetrics: protectedProcedure .input( z.object({ diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index d30b99b3a..baec4d6ac 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -4,6 +4,7 @@ import { findNotificationById, findOrganizationById, findUserById, + getDokployUrl, getUserByToken, IS_CLOUD, removeUserById, @@ -419,11 +420,10 @@ export const userRouter = createTRPCRouter({ }); } - const admin = await findAdmin(); const host = process.env.NODE_ENV === "development" ? "http://localhost:3000" - : admin.user.host; + : await getDokployUrl(); const inviteLink = `${host}/invitation?token=${input.invitationId}`; const organization = await findOrganizationById( diff --git a/apps/dokploy/server/queues/deployments-queue.ts b/apps/dokploy/server/queues/deployments-queue.ts index b8dfb8cd0..4c117e7e3 100644 --- a/apps/dokploy/server/queues/deployments-queue.ts +++ b/apps/dokploy/server/queues/deployments-queue.ts @@ -2,13 +2,8 @@ import { deployApplication, deployCompose, deployPreviewApplication, - deployRemoteApplication, - deployRemoteCompose, - deployRemotePreviewApplication, rebuildApplication, rebuildCompose, - rebuildRemoteApplication, - rebuildRemoteCompose, updateApplicationStatus, updateCompose, updatePreviewDeployment, @@ -24,91 +19,48 @@ export const deploymentWorker = new Worker( if (job.data.applicationType === "application") { await updateApplicationStatus(job.data.applicationId, "running"); - if (job.data.server) { - if (job.data.type === "redeploy") { - await rebuildRemoteApplication({ - applicationId: job.data.applicationId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } else if (job.data.type === "deploy") { - await deployRemoteApplication({ - applicationId: job.data.applicationId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } - } else { - if (job.data.type === "redeploy") { - await rebuildApplication({ - applicationId: job.data.applicationId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } else if (job.data.type === "deploy") { - await deployApplication({ - applicationId: job.data.applicationId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } + if (job.data.type === "redeploy") { + await rebuildApplication({ + applicationId: job.data.applicationId, + titleLog: job.data.titleLog, + descriptionLog: job.data.descriptionLog, + }); + } else if (job.data.type === "deploy") { + await deployApplication({ + applicationId: job.data.applicationId, + titleLog: job.data.titleLog, + descriptionLog: job.data.descriptionLog, + }); } } else if (job.data.applicationType === "compose") { await updateCompose(job.data.composeId, { composeStatus: "running", }); - - if (job.data.server) { - if (job.data.type === "redeploy") { - await rebuildRemoteCompose({ - composeId: job.data.composeId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } else if (job.data.type === "deploy") { - await deployRemoteCompose({ - composeId: job.data.composeId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } - } else { - if (job.data.type === "deploy") { - await deployCompose({ - composeId: job.data.composeId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } else if (job.data.type === "redeploy") { - await rebuildCompose({ - composeId: job.data.composeId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - }); - } + if (job.data.type === "deploy") { + await deployCompose({ + composeId: job.data.composeId, + titleLog: job.data.titleLog, + descriptionLog: job.data.descriptionLog, + }); + } else if (job.data.type === "redeploy") { + await rebuildCompose({ + composeId: job.data.composeId, + titleLog: job.data.titleLog, + descriptionLog: job.data.descriptionLog, + }); } } else if (job.data.applicationType === "application-preview") { await updatePreviewDeployment(job.data.previewDeploymentId, { previewStatus: "running", }); - if (job.data.server) { - if (job.data.type === "deploy") { - await deployRemotePreviewApplication({ - applicationId: job.data.applicationId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - previewDeploymentId: job.data.previewDeploymentId, - }); - } - } else { - if (job.data.type === "deploy") { - await deployPreviewApplication({ - applicationId: job.data.applicationId, - titleLog: job.data.titleLog, - descriptionLog: job.data.descriptionLog, - previewDeploymentId: job.data.previewDeploymentId, - }); - } + + if (job.data.type === "deploy") { + await deployPreviewApplication({ + applicationId: job.data.applicationId, + titleLog: job.data.titleLog, + descriptionLog: job.data.descriptionLog, + previewDeploymentId: job.data.previewDeploymentId, + }); } } } catch (error) { diff --git a/apps/dokploy/server/wss/docker-stats.ts b/apps/dokploy/server/wss/docker-stats.ts index 99e993dce..bd740e976 100644 --- a/apps/dokploy/server/wss/docker-stats.ts +++ b/apps/dokploy/server/wss/docker-stats.ts @@ -2,6 +2,7 @@ import type http from "node:http"; import { docker, execAsync, + getHostSystemStats, getLastAdvancedStatsFile, recordAdvancedStats, validateRequest, @@ -49,6 +50,21 @@ export const setupDockerStatsMonitoringSocketServer = ( } const intervalId = setInterval(async () => { try { + // Special case: when monitoring "dokploy", get host system stats instead of container stats + if (appName === "dokploy") { + const stat = await getHostSystemStats(); + + await recordAdvancedStats(stat, appName); + const data = await getLastAdvancedStatsFile(appName); + + ws.send( + JSON.stringify({ + data, + }), + ); + return; + } + const filter = { status: ["running"], ...(appType === "application" && { diff --git a/apps/dokploy/setup.ts b/apps/dokploy/setup.ts index 55e1da87c..e0ccb86d8 100644 --- a/apps/dokploy/setup.ts +++ b/apps/dokploy/setup.ts @@ -22,7 +22,7 @@ import { await initializeNetwork(); createDefaultTraefikConfig(); createDefaultServerTraefikConfig(); - await execAsync("docker pull traefik:v3.5.0"); + await execAsync("docker pull traefik:v3.6.1"); await initializeStandaloneTraefik(); await initializeRedis(); await initializePostgres(); diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index a58290467..517c71b41 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -6,7 +6,7 @@ // boolean, // } from "drizzle-orm/pg-core"; -// export const users_temp = pgTable("users_temp", { +// export const user = pgTable("user", { // id: text("id").primaryKey(), // name: text("name").notNull(), // email: text("email").notNull().unique(), @@ -29,7 +29,7 @@ // userAgent: text("user_agent"), // userId: text("user_id") // .notNull() -// .references(() => users_temp.id, { onDelete: "cascade" }), +// .references(() => user.id, { onDelete: "cascade" }), // activeOrganizationId: text("active_organization_id"), // }); @@ -39,7 +39,7 @@ // providerId: text("provider_id").notNull(), // userId: text("user_id") // .notNull() -// .references(() => users_temp.id, { onDelete: "cascade" }), +// .references(() => user.id, { onDelete: "cascade" }), // accessToken: text("access_token"), // refreshToken: text("refresh_token"), // idToken: text("id_token"), diff --git a/packages/server/package.json b/packages/server/package.json index 4d0f2e804..077ee3d5d 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -61,7 +61,7 @@ "lodash": "4.17.21", "micromatch": "4.0.8", "nanoid": "3.3.11", - "node-os-utils": "1.3.7", + "node-os-utils": "2.0.1", "node-pty": "1.0.0", "node-schedule": "2.1.1", "nodemailer": "6.9.14", @@ -88,7 +88,6 @@ "@types/lodash": "4.17.4", "@types/micromatch": "4.0.9", "@types/node": "^18.19.104", - "@types/node-os-utils": "1.3.4", "@types/node-schedule": "2.1.6", "@types/nodemailer": "^6.4.17", "@types/qrcode": "^1.5.5", diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index f3d70e680..40789a4a3 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -9,7 +9,7 @@ import { import { nanoid } from "nanoid"; import { projects } from "./project"; import { server } from "./server"; -import { users_temp } from "./user"; +import { user } from "./user"; export const account = pgTable("account", { id: text("id") @@ -21,7 +21,7 @@ export const account = pgTable("account", { providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), @@ -39,9 +39,9 @@ export const account = pgTable("account", { }); export const accountRelations = relations(account, ({ one }) => ({ - user: one(users_temp, { + user: one(user, { fields: [account.userId], - references: [users_temp.id], + references: [user.id], }), })); @@ -65,15 +65,15 @@ export const organization = pgTable("organization", { metadata: text("metadata"), ownerId: text("owner_id") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), }); export const organizationRelations = relations( organization, ({ one, many }) => ({ - owner: one(users_temp, { + owner: one(user, { fields: [organization.ownerId], - references: [users_temp.id], + references: [user.id], }), servers: many(server), projects: many(projects), @@ -90,10 +90,11 @@ export const member = pgTable("member", { .references(() => organization.id, { onDelete: "cascade" }), userId: text("user_id") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), role: text("role").notNull().$type<"owner" | "member" | "admin">(), createdAt: timestamp("created_at").notNull(), teamId: text("team_id"), + isDefault: boolean("is_default").notNull().default(false), // Permissions canCreateProjects: boolean("canCreateProjects").notNull().default(false), canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), @@ -133,9 +134,9 @@ export const memberRelations = relations(member, ({ one }) => ({ fields: [member.organizationId], references: [organization.id], }), - user: one(users_temp, { + user: one(user, { fields: [member.userId], - references: [users_temp.id], + references: [user.id], }), })); @@ -150,7 +151,7 @@ export const invitation = pgTable("invitation", { expiresAt: timestamp("expires_at").notNull(), inviterId: text("inviter_id") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), teamId: text("team_id"), }); @@ -167,7 +168,7 @@ export const twoFactor = pgTable("two_factor", { backupCodes: text("backup_codes").notNull(), userId: text("user_id") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), }); export const apikey = pgTable("apikey", { @@ -178,7 +179,7 @@ export const apikey = pgTable("apikey", { key: text("key").notNull(), userId: text("user_id") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), refillInterval: integer("refill_interval"), refillAmount: integer("refill_amount"), lastRefillAt: timestamp("last_refill_at"), @@ -197,8 +198,8 @@ export const apikey = pgTable("apikey", { }); export const apikeyRelations = relations(apikey, ({ one }) => ({ - user: one(users_temp, { + user: one(user, { fields: [apikey.userId], - references: [users_temp.id], + references: [user.id], }), })); diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 0306387ed..f3e402cc5 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -28,6 +28,8 @@ import { server } from "./server"; import { applicationStatus, certificateType, + type EndpointSpecSwarm, + EndpointSpecSwarmSchema, type HealthCheckSwarm, HealthCheckSwarmSchema, type LabelsSwarm, @@ -167,6 +169,7 @@ export const applications = pgTable("application", { labelsSwarm: json("labelsSwarm").$type(), networkSwarm: json("networkSwarm").$type(), stopGracePeriodSwarm: bigint("stopGracePeriodSwarm", { mode: "bigint" }), + endpointSpecSwarm: json("endpointSpecSwarm").$type(), // replicas: integer("replicas").default(1).notNull(), applicationStatus: applicationStatus("applicationStatus") @@ -318,6 +321,7 @@ const createSchema = createInsertSchema(applications, { previewLabels: z.array(z.string()).optional(), cleanCache: z.boolean().optional(), stopGracePeriodSwarm: z.bigint().nullable(), + endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); export const apiCreateApplication = createSchema.pick({ diff --git a/packages/server/src/db/schema/backups.ts b/packages/server/src/db/schema/backups.ts index 2359a40ad..9b4881924 100644 --- a/packages/server/src/db/schema/backups.ts +++ b/packages/server/src/db/schema/backups.ts @@ -19,7 +19,7 @@ import { mariadb } from "./mariadb"; import { mongo } from "./mongo"; import { mysql } from "./mysql"; import { postgres } from "./postgres"; -import { users_temp } from "./user"; +import { user } from "./user"; export const databaseType = pgEnum("databaseType", [ "postgres", "mariadb", @@ -74,7 +74,7 @@ export const backups = pgTable("backup", { mongoId: text("mongoId").references((): AnyPgColumn => mongo.mongoId, { onDelete: "cascade", }), - userId: text("userId").references(() => users_temp.id), + userId: text("userId").references(() => user.id), // Only for compose backups metadata: jsonb("metadata").$type< | { @@ -118,9 +118,9 @@ export const backupsRelations = relations(backups, ({ one, many }) => ({ fields: [backups.mongoId], references: [mongo.mongoId], }), - user: one(users_temp, { + user: one(user, { fields: [backups.userId], - references: [users_temp.id], + references: [user.id], }), compose: one(compose, { fields: [backups.composeId], diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index dafbc2dac..3d2c7027c 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -8,7 +8,7 @@ import { bitbucket } from "./bitbucket"; import { gitea } from "./gitea"; import { github } from "./github"; import { gitlab } from "./gitlab"; -import { users_temp } from "./user"; +import { user } from "./user"; export const gitProviderType = pgEnum("gitProviderType", [ "github", @@ -32,7 +32,7 @@ export const gitProvider = pgTable("git_provider", { .references(() => organization.id, { onDelete: "cascade" }), userId: text("userId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), }); export const gitProviderRelations = relations(gitProvider, ({ one }) => ({ @@ -56,9 +56,9 @@ export const gitProviderRelations = relations(gitProvider, ({ one }) => ({ fields: [gitProvider.organizationId], references: [organization.id], }), - user: one(users_temp, { + user: one(user, { fields: [gitProvider.userId], - references: [users_temp.id], + references: [user.id], }), })); diff --git a/packages/server/src/db/schema/mariadb.ts b/packages/server/src/db/schema/mariadb.ts index 5dc32f24b..2038d7cf6 100644 --- a/packages/server/src/db/schema/mariadb.ts +++ b/packages/server/src/db/schema/mariadb.ts @@ -9,6 +9,8 @@ import { mounts } from "./mount"; import { server } from "./server"; import { applicationStatus, + type EndpointSpecSwarm, + EndpointSpecSwarmSchema, type HealthCheckSwarm, HealthCheckSwarmSchema, type LabelsSwarm, @@ -63,6 +65,7 @@ export const mariadb = pgTable("mariadb", { labelsSwarm: json("labelsSwarm").$type(), networkSwarm: json("networkSwarm").$type(), stopGracePeriodSwarm: bigint("stopGracePeriodSwarm", { mode: "bigint" }), + endpointSpecSwarm: json("endpointSpecSwarm").$type(), replicas: integer("replicas").default(1).notNull(), createdAt: text("createdAt") .notNull() @@ -130,6 +133,7 @@ const createSchema = createInsertSchema(mariadb, { labelsSwarm: LabelsSwarmSchema.nullable(), networkSwarm: NetworkSwarmSchema.nullable(), stopGracePeriodSwarm: z.bigint().nullable(), + endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); export const apiCreateMariaDB = createSchema diff --git a/packages/server/src/db/schema/mongo.ts b/packages/server/src/db/schema/mongo.ts index c1ee4185b..f3b15d3a7 100644 --- a/packages/server/src/db/schema/mongo.ts +++ b/packages/server/src/db/schema/mongo.ts @@ -16,6 +16,8 @@ import { mounts } from "./mount"; import { server } from "./server"; import { applicationStatus, + type EndpointSpecSwarm, + EndpointSpecSwarmSchema, type HealthCheckSwarm, HealthCheckSwarmSchema, type LabelsSwarm, @@ -66,6 +68,7 @@ export const mongo = pgTable("mongo", { labelsSwarm: json("labelsSwarm").$type(), networkSwarm: json("networkSwarm").$type(), stopGracePeriodSwarm: bigint("stopGracePeriodSwarm", { mode: "bigint" }), + endpointSpecSwarm: json("endpointSpecSwarm").$type(), replicas: integer("replicas").default(1).notNull(), createdAt: text("createdAt") .notNull() @@ -127,6 +130,7 @@ const createSchema = createInsertSchema(mongo, { labelsSwarm: LabelsSwarmSchema.nullable(), networkSwarm: NetworkSwarmSchema.nullable(), stopGracePeriodSwarm: z.bigint().nullable(), + endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); export const apiCreateMongo = createSchema diff --git a/packages/server/src/db/schema/mysql.ts b/packages/server/src/db/schema/mysql.ts index c90048980..84d071b58 100644 --- a/packages/server/src/db/schema/mysql.ts +++ b/packages/server/src/db/schema/mysql.ts @@ -9,6 +9,8 @@ import { mounts } from "./mount"; import { server } from "./server"; import { applicationStatus, + type EndpointSpecSwarm, + EndpointSpecSwarmSchema, type HealthCheckSwarm, HealthCheckSwarmSchema, type LabelsSwarm, @@ -61,6 +63,7 @@ export const mysql = pgTable("mysql", { labelsSwarm: json("labelsSwarm").$type(), networkSwarm: json("networkSwarm").$type(), stopGracePeriodSwarm: bigint("stopGracePeriodSwarm", { mode: "bigint" }), + endpointSpecSwarm: json("endpointSpecSwarm").$type(), replicas: integer("replicas").default(1).notNull(), createdAt: text("createdAt") .notNull() @@ -127,6 +130,7 @@ const createSchema = createInsertSchema(mysql, { labelsSwarm: LabelsSwarmSchema.nullable(), networkSwarm: NetworkSwarmSchema.nullable(), stopGracePeriodSwarm: z.bigint().nullable(), + endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); export const apiCreateMySql = createSchema diff --git a/packages/server/src/db/schema/postgres.ts b/packages/server/src/db/schema/postgres.ts index 375f7a9c0..98fa2a7c8 100644 --- a/packages/server/src/db/schema/postgres.ts +++ b/packages/server/src/db/schema/postgres.ts @@ -9,6 +9,8 @@ import { mounts } from "./mount"; import { server } from "./server"; import { applicationStatus, + type EndpointSpecSwarm, + EndpointSpecSwarmSchema, type HealthCheckSwarm, HealthCheckSwarmSchema, type LabelsSwarm, @@ -61,6 +63,7 @@ export const postgres = pgTable("postgres", { labelsSwarm: json("labelsSwarm").$type(), networkSwarm: json("networkSwarm").$type(), stopGracePeriodSwarm: bigint("stopGracePeriodSwarm", { mode: "bigint" }), + endpointSpecSwarm: json("endpointSpecSwarm").$type(), replicas: integer("replicas").default(1).notNull(), createdAt: text("createdAt") .notNull() @@ -120,6 +123,7 @@ const createSchema = createInsertSchema(postgres, { labelsSwarm: LabelsSwarmSchema.nullable(), networkSwarm: NetworkSwarmSchema.nullable(), stopGracePeriodSwarm: z.bigint().nullable(), + endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); export const apiCreatePostgres = createSchema diff --git a/packages/server/src/db/schema/redis.ts b/packages/server/src/db/schema/redis.ts index 0cb6dd484..2d17f1df1 100644 --- a/packages/server/src/db/schema/redis.ts +++ b/packages/server/src/db/schema/redis.ts @@ -5,10 +5,11 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { environments } from "./environment"; import { mounts } from "./mount"; -import { projects } from "./project"; import { server } from "./server"; import { applicationStatus, + type EndpointSpecSwarm, + EndpointSpecSwarmSchema, type HealthCheckSwarm, HealthCheckSwarmSchema, type LabelsSwarm, @@ -61,6 +62,7 @@ export const redis = pgTable("redis", { labelsSwarm: json("labelsSwarm").$type(), networkSwarm: json("networkSwarm").$type(), stopGracePeriodSwarm: bigint("stopGracePeriodSwarm", { mode: "bigint" }), + endpointSpecSwarm: json("endpointSpecSwarm").$type(), replicas: integer("replicas").default(1).notNull(), environmentId: text("environmentId") @@ -110,6 +112,7 @@ const createSchema = createInsertSchema(redis, { labelsSwarm: LabelsSwarmSchema.nullable(), networkSwarm: NetworkSwarmSchema.nullable(), stopGracePeriodSwarm: z.bigint().nullable(), + endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); export const apiCreateRedis = createSchema diff --git a/packages/server/src/db/schema/schedule.ts b/packages/server/src/db/schema/schedule.ts index 0f078ae79..80abc3524 100644 --- a/packages/server/src/db/schema/schedule.ts +++ b/packages/server/src/db/schema/schedule.ts @@ -7,7 +7,7 @@ import { applications } from "./application"; import { compose } from "./compose"; import { deployments } from "./deployment"; import { server } from "./server"; -import { users_temp } from "./user"; +import { user } from "./user"; import { generateAppName } from "./utils"; export const shellTypes = pgEnum("shellType", ["bash", "sh"]); @@ -45,7 +45,7 @@ export const schedules = pgTable("schedule", { serverId: text("serverId").references(() => server.serverId, { onDelete: "cascade", }), - userId: text("userId").references(() => users_temp.id, { + userId: text("userId").references(() => user.id, { onDelete: "cascade", }), enabled: boolean("enabled").notNull().default(true), @@ -69,9 +69,9 @@ export const schedulesRelations = relations(schedules, ({ one, many }) => ({ fields: [schedules.serverId], references: [server.serverId], }), - user: one(users_temp, { + user: one(user, { fields: [schedules.userId], - references: [users_temp.id], + references: [user.id], }), deployments: many(deployments), })); diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index f7c12dae0..971bb25bd 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -1,5 +1,5 @@ import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { users_temp } from "./user"; +import { user } from "./user"; // OLD TABLE export const session = pgTable("session_temp", { @@ -12,7 +12,7 @@ export const session = pgTable("session_temp", { userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), impersonatedBy: text("impersonated_by"), activeOrganizationId: text("active_organization_id"), }); diff --git a/packages/server/src/db/schema/shared.ts b/packages/server/src/db/schema/shared.ts index ab42aa1c1..600593d7a 100644 --- a/packages/server/src/db/schema/shared.ts +++ b/packages/server/src/db/schema/shared.ts @@ -74,6 +74,18 @@ export interface LabelsSwarm { [name: string]: string; } +export interface EndpointPortConfigSwarm { + Protocol?: string | undefined; + TargetPort?: number | undefined; + PublishedPort?: number | undefined; + PublishMode?: string | undefined; +} + +export interface EndpointSpecSwarm { + Mode?: string | undefined; + Ports?: EndpointPortConfigSwarm[] | undefined; +} + export const HealthCheckSwarmSchema = z .object({ Test: z.array(z.string()).optional(), @@ -161,3 +173,19 @@ export const NetworkSwarmSchema = z.array( ); export const LabelsSwarmSchema = z.record(z.string()); + +export const EndpointPortConfigSwarmSchema = z + .object({ + Protocol: z.string().optional(), + TargetPort: z.number().optional(), + PublishedPort: z.number().optional(), + PublishMode: z.string().optional(), + }) + .strict(); + +export const EndpointSpecSwarmSchema = z + .object({ + Mode: z.string().optional(), + Ports: z.array(EndpointPortConfigSwarmSchema).optional(), + }) + .strict(); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 9b4ab78e9..6627782df 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -26,7 +26,7 @@ import { certificateType } from "./shared"; // OLD TABLE // TEMP -export const users_temp = pgTable("user_temp", { +export const user = pgTable("user", { id: text("id") .notNull() .primaryKey() @@ -122,9 +122,9 @@ export const users_temp = pgTable("user_temp", { serversQuantity: integer("serversQuantity").notNull().default(0), }); -export const usersRelations = relations(users_temp, ({ one, many }) => ({ +export const usersRelations = relations(user, ({ one, many }) => ({ account: one(account, { - fields: [users_temp.id], + fields: [user.id], references: [account.userId], }), organizations: many(organization), @@ -134,7 +134,7 @@ export const usersRelations = relations(users_temp, ({ one, many }) => ({ schedules: many(schedules), })); -const createSchema = createInsertSchema(users_temp, { +const createSchema = createInsertSchema(user, { id: z.string().min(1), isRegistered: z.boolean().optional(), }).omit({ diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 739a666f7..3ce060c58 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -165,6 +165,7 @@ const { handler, api } = betterAuth({ organizationId: organization?.id || "", role: "owner", createdAt: new Date(), + isDefault: true, // Mark first organization as default }); }); } @@ -174,9 +175,14 @@ const { handler, api } = betterAuth({ session: { create: { before: async (session) => { + // Find the default organization for this user + // Priority: 1) isDefault=true, 2) most recently created const member = await db.query.member.findFirst({ where: eq(schema.member.userId, session.userId), - orderBy: desc(schema.member.createdAt), + orderBy: [ + desc(schema.member.isDefault), + desc(schema.member.createdAt), + ], with: { organization: true, }, @@ -197,7 +203,7 @@ const { handler, api } = betterAuth({ updateAge: 60 * 60 * 24, }, user: { - modelName: "users_temp", + modelName: "user", additionalFields: { role: { type: "string", diff --git a/packages/server/src/monitoring/utils.ts b/packages/server/src/monitoring/utils.ts index 11ebb6169..2c42b99a6 100644 --- a/packages/server/src/monitoring/utils.ts +++ b/packages/server/src/monitoring/utils.ts @@ -1,5 +1,5 @@ import { promises } from "node:fs"; -import osUtils from "node-os-utils"; +import { OSUtils } from "node-os-utils"; import { paths } from "../constants"; export interface Container { @@ -38,22 +38,122 @@ export const recordAdvancedStats = async ( }); if (appName === "dokploy") { - const disk = await osUtils.drive.info("/"); + const osutils = new OSUtils(); + const diskResult = await osutils.disk.usageByMountPoint("/"); - const diskUsage = disk.usedGb; - const diskTotal = disk.totalGb; - const diskUsedPercentage = disk.usedPercentage; - const diskFree = disk.freeGb; + if (diskResult.success && diskResult.data) { + const disk = diskResult.data; + const diskUsage = disk.used.toGB().toFixed(2); + const diskTotal = disk.total.toGB().toFixed(2); + const diskUsedPercentage = disk.usagePercentage; + const diskFree = disk.available.toGB().toFixed(2); - await updateStatsFile(appName, "disk", { - diskTotal: +diskTotal, - diskUsedPercentage: +diskUsedPercentage, - diskUsage: +diskUsage, - diskFree: +diskFree, - }); + await updateStatsFile(appName, "disk", { + diskTotal: +diskTotal, + diskUsedPercentage: +diskUsedPercentage, + diskUsage: +diskUsage, + diskFree: +diskFree, + }); + } } }; +/** + * Get host system statistics using node-os-utils + * This is used when monitoring "dokploy" to show host stats instead of container stats + */ +export const getHostSystemStats = async (): Promise => { + const osutils = new OSUtils({ + disk: { + includeStats: true, // Enable disk I/O statistics + }, + }); + + // Get CPU usage + const cpuResult = await osutils.cpu.usage(); + const cpuUsage = cpuResult.success ? cpuResult.data : 0; + + // Get memory info + const memResult = await osutils.memory.info(); + let memUsedGB = 0; + let memTotalGB = 0; + let memUsedPercent = 0; + if (memResult.success) { + memTotalGB = memResult.data.total.toGB(); + memUsedGB = memResult.data.used.toGB(); + memUsedPercent = memResult.data.usagePercentage; + } + + // Get network stats from network.overview() + let netInputBytes = 0; + let netOutputBytes = 0; + const networkOverview = await osutils.network.overview(); + if (networkOverview.success) { + netInputBytes = networkOverview.data.totalRxBytes.toBytes(); + netOutputBytes = networkOverview.data.totalTxBytes.toBytes(); + } + + // Get Block I/O from disk.stats() + let blockReadBytes = 0; + let blockWriteBytes = 0; + const diskStats = await osutils.disk.stats(); + if (diskStats.success && diskStats.data.length > 0) { + // Filter out virtual devices (loop, ram, sr, etc.) - only include real disk devices + const excludePatterns = [/^loop/, /^ram/, /^sr\d+$/, /^fd\d+$/]; + for (const stat of diskStats.data) { + // Skip virtual devices + if ( + stat.device && + excludePatterns.some((pattern) => pattern.test(stat.device)) + ) { + continue; + } + // readBytes and writeBytes are DataSize objects with .toBytes() method + blockReadBytes += stat.readBytes.toBytes(); + blockWriteBytes += stat.writeBytes.toBytes(); + } + } + + // Format values similar to docker stats + const formatBytes = (bytes: number): string => { + if (bytes >= 1024 * 1024 * 1024) { + return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GiB`; + } + if (bytes >= 1024 * 1024) { + return `${(bytes / (1024 * 1024)).toFixed(2)}MiB`; + } + if (bytes >= 1024) { + return `${(bytes / 1024).toFixed(2)}KiB`; + } + return `${bytes}B`; + }; + + // Format memory usage similar to docker stats format: "used / total" + const memUsedFormatted = `${memUsedGB.toFixed(2)}GiB`; + const memTotalFormatted = `${memTotalGB.toFixed(2)}GiB`; + const memUsageFormatted = `${memUsedFormatted} / ${memTotalFormatted}`; + + // Format network I/O + const netInputMb = netInputBytes / (1024 * 1024); + const netOutputMb = netOutputBytes / (1024 * 1024); + const netIOFormatted = `${netInputMb.toFixed(2)}MB / ${netOutputMb.toFixed(2)}MB`; + + // Format Block I/O + const blockIOFormatted = `${formatBytes(blockReadBytes)} / ${formatBytes(blockWriteBytes)}`; + + // Create a stat object compatible with recordAdvancedStats + return { + CPUPerc: `${cpuUsage.toFixed(2)}%`, + MemPerc: `${memUsedPercent.toFixed(2)}%`, + MemUsage: memUsageFormatted, + BlockIO: blockIOFormatted, + NetIO: netIOFormatted, + Container: "dokploy", + ID: "host-system", + Name: "dokploy", + }; +}; + export const getAdvancedStats = async (appName: string) => { return { cpu: await readStatsFile(appName, "cpu"), diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index f6ce9614f..0cbb20785 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -3,26 +3,26 @@ import { invitation, member, organization, - users_temp, + user, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { IS_CLOUD } from "../constants"; export const findUserById = async (userId: string) => { - const user = await db.query.users_temp.findFirst({ - where: eq(users_temp.id, userId), + const userResult = await db.query.user.findFirst({ + where: eq(user.id, userId), // with: { // account: true, // }, }); - if (!user) { + if (!userResult) { throw new TRPCError({ code: "NOT_FOUND", message: "User not found", }); } - return user; + return userResult; }; export const findOrganizationById = async (organizationId: string) => { @@ -64,7 +64,7 @@ export const findAdmin = async () => { }; export const getUserByToken = async (token: string) => { - const user = await db.query.invitation.findFirst({ + const userResult = await db.query.invitation.findFirst({ where: eq(invitation.id, token), columns: { id: true, @@ -76,29 +76,29 @@ export const getUserByToken = async (token: string) => { }, }); - if (!user) { + if (!userResult) { throw new TRPCError({ code: "NOT_FOUND", message: "Invitation not found", }); } - const userAlreadyExists = await db.query.users_temp.findFirst({ - where: eq(users_temp.email, user?.email || ""), + const userAlreadyExists = await db.query.user.findFirst({ + where: eq(user.email, userResult?.email || ""), }); - const { expiresAt, ...rest } = user; + const { expiresAt, ...rest } = userResult; return { ...rest, - isExpired: user.expiresAt < new Date(), + isExpired: userResult.expiresAt < new Date(), userAlreadyExists: !!userAlreadyExists, }; }; export const removeUserById = async (userId: string) => { await db - .delete(users_temp) - .where(eq(users_temp.id, userId)) + .delete(user) + .where(eq(user.id, userId)) .returning() .then((res) => res[0]); }; @@ -110,7 +110,8 @@ export const getDokployUrl = async () => { const admin = await findAdmin(); if (admin.user.host) { - return `https://${admin.user.host}`; + const protocol = admin.user.https ? "https" : "http"; + return `${protocol}://${admin.user.host}`; } return `http://${admin.user.serverIp}:${process.env.PORT}`; }; diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index 1891f9b6b..c10babe56 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -7,45 +7,32 @@ import { } from "@dokploy/server/db/schema"; import { getAdvancedStats } from "@dokploy/server/monitoring/utils"; import { - buildApplication, getBuildCommand, mechanizeDockerContainer, } from "@dokploy/server/utils/builders"; import { sendBuildErrorNotifications } from "@dokploy/server/utils/notifications/build-error"; import { sendBuildSuccessNotifications } from "@dokploy/server/utils/notifications/build-success"; -import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; import { - cloneBitbucketRepository, - getBitbucketCloneCommand, -} from "@dokploy/server/utils/providers/bitbucket"; -import { - buildDocker, - buildRemoteDocker, -} from "@dokploy/server/utils/providers/docker"; + execAsync, + execAsyncRemote, +} from "@dokploy/server/utils/process/execAsync"; +import { cloneBitbucketRepository } from "@dokploy/server/utils/providers/bitbucket"; +import { buildRemoteDocker } from "@dokploy/server/utils/providers/docker"; import { cloneGitRepository, - getCustomGitCloneCommand, + getGitCommitInfo, } from "@dokploy/server/utils/providers/git"; -import { - cloneGiteaRepository, - getGiteaCloneCommand, -} from "@dokploy/server/utils/providers/gitea"; -import { - cloneGithubRepository, - getGithubCloneCommand, -} from "@dokploy/server/utils/providers/github"; -import { - cloneGitlabRepository, - getGitlabCloneCommand, -} from "@dokploy/server/utils/providers/gitlab"; +import { cloneGiteaRepository } from "@dokploy/server/utils/providers/gitea"; +import { cloneGithubRepository } from "@dokploy/server/utils/providers/github"; +import { cloneGitlabRepository } from "@dokploy/server/utils/providers/gitlab"; import { createTraefikConfig } from "@dokploy/server/utils/traefik/application"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -import { encodeBase64 } from "../utils/docker/utils"; import { getDokployUrl } from "./admin"; import { createDeployment, createDeploymentPreview, + updateDeployment, updateDeploymentStatus, } from "./deployment"; import { type Domain, getDomainHost } from "./domain"; @@ -192,30 +179,31 @@ export const deployApplication = async ({ }); try { + let command = "set -e;"; if (application.sourceType === "github") { - await cloneGithubRepository({ - ...application, - logPath: deployment.logPath, - }); - await buildApplication(application, deployment.logPath); + command += await cloneGithubRepository(application); } else if (application.sourceType === "gitlab") { - await cloneGitlabRepository(application, deployment.logPath); - await buildApplication(application, deployment.logPath); + command += await cloneGitlabRepository(application); } else if (application.sourceType === "gitea") { - await cloneGiteaRepository(application, deployment.logPath); - await buildApplication(application, deployment.logPath); + command += await cloneGiteaRepository(application); } else if (application.sourceType === "bitbucket") { - await cloneBitbucketRepository(application, deployment.logPath); - await buildApplication(application, deployment.logPath); - } else if (application.sourceType === "docker") { - await buildDocker(application, deployment.logPath); + command += await cloneBitbucketRepository(application); } else if (application.sourceType === "git") { - await cloneGitRepository(application, deployment.logPath); - await buildApplication(application, deployment.logPath); - } else if (application.sourceType === "drop") { - await buildApplication(application, deployment.logPath); + command += await cloneGitRepository(application); + } else if (application.sourceType === "docker") { + command += await buildRemoteDocker(application); } + command += getBuildCommand(application); + + const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; + if (application.serverId) { + await execAsyncRemote(application.serverId, commandWithLog); + } else { + await execAsync(commandWithLog); + } + + await mechanizeDockerContainer(application); await updateDeploymentStatus(deployment.deploymentId, "done"); await updateApplicationStatus(applicationId, "done"); @@ -239,6 +227,12 @@ export const deployApplication = async ({ domains: application.domains, }); } catch (error) { + const command = `echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath};`; + if (application.serverId) { + await execAsyncRemote(application.serverId, command); + } else { + await execAsync(command); + } await updateDeploymentStatus(deployment.deploymentId, "error"); await updateApplicationStatus(applicationId, "error"); @@ -253,8 +247,19 @@ export const deployApplication = async ({ }); throw error; - } + } finally { + // Only extract commit info for non-docker sources + if (application.sourceType !== "docker") { + const commitInfo = await getGitCommitInfo(application); + if (commitInfo) { + await updateDeployment(deployment.deploymentId, { + title: commitInfo.message, + description: `Commit: ${commitInfo.hash}`, + }); + } + } + } return true; }; @@ -276,129 +281,21 @@ export const rebuildApplication = async ({ }); try { - if (application.sourceType === "github") { - await buildApplication(application, deployment.logPath); - } else if (application.sourceType === "gitlab") { - await buildApplication(application, deployment.logPath); - } else if (application.sourceType === "bitbucket") { - await buildApplication(application, deployment.logPath); - } else if (application.sourceType === "docker") { - await buildDocker(application, deployment.logPath); - } else if (application.sourceType === "git") { - await buildApplication(application, deployment.logPath); - } else if (application.sourceType === "drop") { - await buildApplication(application, deployment.logPath); - } - await updateDeploymentStatus(deployment.deploymentId, "done"); - await updateApplicationStatus(applicationId, "done"); - } catch (error) { - await updateDeploymentStatus(deployment.deploymentId, "error"); - await updateApplicationStatus(applicationId, "error"); - throw error; - } - - return true; -}; - -export const deployRemoteApplication = async ({ - applicationId, - titleLog = "Manual deployment", - descriptionLog = "", -}: { - applicationId: string; - titleLog: string; - descriptionLog: string; -}) => { - const application = await findApplicationById(applicationId); - - const buildLink = `${await getDokployUrl()}/dashboard/project/${application.environment.projectId}/environment/${application.environmentId}/services/application/${application.applicationId}?tab=deployments`; - const deployment = await createDeployment({ - applicationId: applicationId, - title: titleLog, - description: descriptionLog, - }); - - try { + let command = "set -e;"; + // Check case for docker only + command += getBuildCommand(application); + const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; if (application.serverId) { - let command = "set -e;"; - if (application.sourceType === "github") { - command += await getGithubCloneCommand({ - ...application, - serverId: application.serverId, - logPath: deployment.logPath, - }); - } else if (application.sourceType === "gitlab") { - command += await getGitlabCloneCommand(application, deployment.logPath); - } else if (application.sourceType === "bitbucket") { - command += await getBitbucketCloneCommand( - application, - deployment.logPath, - ); - } else if (application.sourceType === "gitea") { - command += await getGiteaCloneCommand(application, deployment.logPath); - } else if (application.sourceType === "git") { - command += await getCustomGitCloneCommand( - application, - deployment.logPath, - ); - } else if (application.sourceType === "docker") { - command += await buildRemoteDocker(application, deployment.logPath); - } - - if (application.sourceType !== "docker") { - command += getBuildCommand(application, deployment.logPath); - } - await execAsyncRemote(application.serverId, command); - await mechanizeDockerContainer(application); + await execAsyncRemote(application.serverId, commandWithLog); + } else { + await execAsync(commandWithLog); } - + await mechanizeDockerContainer(application); await updateDeploymentStatus(deployment.deploymentId, "done"); await updateApplicationStatus(applicationId, "done"); - - if (application.rollbackActive) { - const tagImage = - application.sourceType === "docker" - ? application.dockerImage - : application.appName; - await createRollback({ - appName: tagImage || "", - deploymentId: deployment.deploymentId, - }); - } - - await sendBuildSuccessNotifications({ - projectName: application.environment.project.name, - applicationName: application.name, - applicationType: "application", - buildLink, - organizationId: application.environment.project.organizationId, - domains: application.domains, - }); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - - const encodedContent = encodeBase64(errorMessage); - - await execAsyncRemote( - application.serverId, - ` - echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath}; - echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath}; - echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`, - ); - await updateDeploymentStatus(deployment.deploymentId, "error"); await updateApplicationStatus(applicationId, "error"); - - await sendBuildErrorNotifications({ - projectName: application.environment.project.name, - applicationName: application.name, - applicationType: "application", - errorMessage: `Please check the logs for details: ${errorMessage}`, - buildLink, - organizationId: application.environment.project.organizationId, - }); - throw error; } @@ -475,14 +372,22 @@ export const deployPreviewApplication = async ({ application.buildArgs = `${application.previewBuildArgs}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain?.host}`; application.buildSecrets = `${application.previewBuildSecrets}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain?.host}`; + let command = "set -e;"; if (application.sourceType === "github") { - await cloneGithubRepository({ + command += await cloneGithubRepository({ ...application, appName: previewDeployment.appName, branch: previewDeployment.branch, - logPath: deployment.logPath, }); - await buildApplication(application, deployment.logPath); + command += getBuildCommand(application); + + const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; + if (application.serverId) { + await execAsyncRemote(application.serverId, commandWithLog); + } else { + await execAsync(commandWithLog); + } + await mechanizeDockerContainer(application); } const successComment = getIssueComment( application.name, @@ -513,170 +418,10 @@ export const deployPreviewApplication = async ({ return true; }; -export const deployRemotePreviewApplication = async ({ - applicationId, - titleLog = "Preview Deployment", - descriptionLog = "", - previewDeploymentId, -}: { - applicationId: string; - titleLog: string; - descriptionLog: string; - previewDeploymentId: string; -}) => { - const application = await findApplicationById(applicationId); - - const deployment = await createDeploymentPreview({ - title: titleLog, - description: descriptionLog, - previewDeploymentId: previewDeploymentId, - }); - - const previewDeployment = - await findPreviewDeploymentById(previewDeploymentId); - - await updatePreviewDeployment(previewDeploymentId, { - createdAt: new Date().toISOString(), - }); - - const previewDomain = getDomainHost(previewDeployment?.domain as Domain); - const issueParams = { - owner: application?.owner || "", - repository: application?.repository || "", - issue_number: previewDeployment.pullRequestNumber, - comment_id: Number.parseInt(previewDeployment.pullRequestCommentId), - githubId: application?.githubId || "", - }; - try { - const commentExists = await issueCommentExists({ - ...issueParams, - }); - if (!commentExists) { - const result = await createPreviewDeploymentComment({ - ...issueParams, - previewDomain, - appName: previewDeployment.appName, - githubId: application?.githubId || "", - previewDeploymentId, - }); - - if (!result) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Pull request comment not found", - }); - } - - issueParams.comment_id = Number.parseInt(result?.pullRequestCommentId); - } - const buildingComment = getIssueComment( - application.name, - "running", - previewDomain, - ); - await updateIssueComment({ - ...issueParams, - body: `### Dokploy Preview Deployment\n\n${buildingComment}`, - }); - application.appName = previewDeployment.appName; - application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain?.host}`; - application.buildArgs = `${application.previewBuildArgs}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain?.host}`; - application.buildSecrets = `${application.previewBuildSecrets}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain?.host}`; - - if (application.serverId) { - let command = "set -e;"; - if (application.sourceType === "github") { - command += await getGithubCloneCommand({ - ...application, - appName: previewDeployment.appName, - branch: previewDeployment.branch, - serverId: application.serverId, - logPath: deployment.logPath, - }); - } - - command += getBuildCommand(application, deployment.logPath); - await execAsyncRemote(application.serverId, command); - await mechanizeDockerContainer(application); - } - - const successComment = getIssueComment( - application.name, - "success", - previewDomain, - ); - await updateIssueComment({ - ...issueParams, - body: `### Dokploy Preview Deployment\n\n${successComment}`, - }); - await updateDeploymentStatus(deployment.deploymentId, "done"); - await updatePreviewDeployment(previewDeploymentId, { - previewStatus: "done", - }); - } catch (error) { - const comment = getIssueComment(application.name, "error", previewDomain); - await updateIssueComment({ - ...issueParams, - body: `### Dokploy Preview Deployment\n\n${comment}`, - }); - await updateDeploymentStatus(deployment.deploymentId, "error"); - await updatePreviewDeployment(previewDeploymentId, { - previewStatus: "error", - }); - throw error; - } - - return true; -}; - -export const rebuildRemoteApplication = async ({ - applicationId, - titleLog = "Rebuild deployment", - descriptionLog = "", -}: { - applicationId: string; - titleLog: string; - descriptionLog: string; -}) => { - const application = await findApplicationById(applicationId); - - const deployment = await createDeployment({ - applicationId: applicationId, - title: titleLog, - description: descriptionLog, - }); - - try { - if (application.serverId) { - if (application.sourceType !== "docker") { - let command = "set -e;"; - command += getBuildCommand(application, deployment.logPath); - await execAsyncRemote(application.serverId, command); - } - await mechanizeDockerContainer(application); - } - await updateDeploymentStatus(deployment.deploymentId, "done"); - await updateApplicationStatus(applicationId, "done"); - } catch (error) { - // @ts-ignore - const encodedContent = encodeBase64(error?.message); - - await execAsyncRemote( - application.serverId, - ` - echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath}; - echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath}; - echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`, - ); - await updateDeploymentStatus(deployment.deploymentId, "error"); - await updateApplicationStatus(applicationId, "error"); - throw error; - } - - return true; -}; - export const getApplicationStats = async (appName: string) => { + if (appName === "dokploy") { + return await getAdvancedStats(appName); + } const filter = { status: ["running"], label: [`com.docker.swarm.service.name=${appName}`], diff --git a/packages/server/src/services/cdn.ts b/packages/server/src/services/cdn.ts index 1e19652c1..234405b8e 100644 --- a/packages/server/src/services/cdn.ts +++ b/packages/server/src/services/cdn.ts @@ -616,6 +616,7 @@ const ARVANCLOUD_IP_RANGES = [ "37.32.18.0/27", "37.32.19.0/27", "185.215.232.0/22", + "178.131.120.48/28", ]; const CDN_PROVIDERS: CDNProvider[] = [ diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 1436c52cc..519a0c404 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -7,14 +7,10 @@ import { cleanAppName, compose, } from "@dokploy/server/db/schema"; -import { - buildCompose, - getBuildComposeCommand, -} from "@dokploy/server/utils/builders/compose"; +import { getBuildComposeCommand } from "@dokploy/server/utils/builders/compose"; import { randomizeSpecificationFile } from "@dokploy/server/utils/docker/compose"; import { cloneCompose, - cloneComposeRemote, loadDockerCompose, loadDockerComposeRemote, } from "@dokploy/server/utils/docker/domain"; @@ -25,35 +21,23 @@ import { execAsync, execAsyncRemote, } from "@dokploy/server/utils/process/execAsync"; -import { - cloneBitbucketRepository, - getBitbucketCloneCommand, -} from "@dokploy/server/utils/providers/bitbucket"; +import { cloneBitbucketRepository } from "@dokploy/server/utils/providers/bitbucket"; import { cloneGitRepository, - getCustomGitCloneCommand, + getGitCommitInfo, } from "@dokploy/server/utils/providers/git"; -import { - cloneGiteaRepository, - getGiteaCloneCommand, -} from "@dokploy/server/utils/providers/gitea"; -import { - cloneGithubRepository, - getGithubCloneCommand, -} from "@dokploy/server/utils/providers/github"; -import { - cloneGitlabRepository, - getGitlabCloneCommand, -} from "@dokploy/server/utils/providers/gitlab"; -import { - createComposeFile, - getCreateComposeFileCommand, -} from "@dokploy/server/utils/providers/raw"; +import { cloneGiteaRepository } from "@dokploy/server/utils/providers/gitea"; +import { cloneGithubRepository } from "@dokploy/server/utils/providers/github"; +import { cloneGitlabRepository } from "@dokploy/server/utils/providers/gitlab"; +import { getCreateComposeFileCommand } from "@dokploy/server/utils/providers/raw"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -import { encodeBase64 } from "../utils/docker/utils"; import { getDokployUrl } from "./admin"; -import { createDeploymentCompose, updateDeploymentStatus } from "./deployment"; +import { + createDeploymentCompose, + updateDeployment, + updateDeploymentStatus, +} from "./deployment"; import { validUniqueServerAppName } from "./project"; export type Compose = typeof compose.$inferSelect; @@ -163,10 +147,11 @@ export const loadServices = async ( const compose = await findComposeById(composeId); if (type === "fetch") { + const command = await cloneCompose(compose); if (compose.serverId) { - await cloneComposeRemote(compose); + await execAsyncRemote(compose.serverId, command); } else { - await cloneCompose(compose); + await execAsync(command); } } @@ -235,24 +220,41 @@ export const deployCompose = async ({ }); try { + const entity = { + ...compose, + type: "compose" as const, + }; + let command = "set -e;"; if (compose.sourceType === "github") { - await cloneGithubRepository({ - ...compose, - logPath: deployment.logPath, - type: "compose", - }); + command += await cloneGithubRepository(entity); } else if (compose.sourceType === "gitlab") { - await cloneGitlabRepository(compose, deployment.logPath, true); + command += await cloneGitlabRepository(entity); } else if (compose.sourceType === "bitbucket") { - await cloneBitbucketRepository(compose, deployment.logPath, true); + command += await cloneBitbucketRepository(entity); } else if (compose.sourceType === "git") { - await cloneGitRepository(compose, deployment.logPath, true); + command += await cloneGitRepository(entity); } else if (compose.sourceType === "gitea") { - await cloneGiteaRepository(compose, deployment.logPath, true); + command += await cloneGiteaRepository(entity); } else if (compose.sourceType === "raw") { - await createComposeFile(compose, deployment.logPath); + command += getCreateComposeFileCommand(entity); } - await buildCompose(compose, deployment.logPath); + + let commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; + if (compose.serverId) { + await execAsyncRemote(compose.serverId, commandWithLog); + } else { + await execAsync(commandWithLog); + } + + command = "set -e;"; + command += await getBuildComposeCommand(entity); + commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; + if (compose.serverId) { + await execAsyncRemote(compose.serverId, commandWithLog); + } else { + await execAsync(commandWithLog); + } + await updateDeploymentStatus(deployment.deploymentId, "done"); await updateCompose(composeId, { composeStatus: "done", @@ -281,6 +283,19 @@ export const deployCompose = async ({ organizationId: compose.environment.project.organizationId, }); throw error; + } finally { + if (compose.sourceType !== "raw") { + const commitInfo = await getGitCommitInfo({ + ...compose, + type: "compose", + }); + if (commitInfo) { + await updateDeployment(deployment.deploymentId, { + title: commitInfo.message, + description: `Commit: ${commitInfo.hash}`, + }); + } + } } }; @@ -302,154 +317,23 @@ export const rebuildCompose = async ({ }); try { + let command = "set -e;"; if (compose.sourceType === "raw") { - await createComposeFile(compose, deployment.logPath); + command += getCreateComposeFileCommand(compose); } - await buildCompose(compose, deployment.logPath); - await updateDeploymentStatus(deployment.deploymentId, "done"); - await updateCompose(composeId, { - composeStatus: "done", - }); - } catch (error) { - await updateDeploymentStatus(deployment.deploymentId, "error"); - await updateCompose(composeId, { - composeStatus: "error", - }); - throw error; - } - - return true; -}; - -export const deployRemoteCompose = async ({ - composeId, - titleLog = "Manual deployment", - descriptionLog = "", -}: { - composeId: string; - titleLog: string; - descriptionLog: string; -}) => { - const compose = await findComposeById(composeId); - - const buildLink = `${await getDokployUrl()}/dashboard/project/${ - compose.environment.projectId - }/environment/${compose.environmentId}/services/compose/${compose.composeId}?tab=deployments`; - const deployment = await createDeploymentCompose({ - composeId: composeId, - title: titleLog, - description: descriptionLog, - }); - try { + let commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; if (compose.serverId) { - let command = "set -e;"; - - if (compose.sourceType === "github") { - command += await getGithubCloneCommand({ - ...compose, - logPath: deployment.logPath, - type: "compose", - serverId: compose.serverId, - }); - } else if (compose.sourceType === "gitlab") { - command += await getGitlabCloneCommand( - compose, - deployment.logPath, - true, - ); - } else if (compose.sourceType === "bitbucket") { - command += await getBitbucketCloneCommand( - compose, - deployment.logPath, - true, - ); - } else if (compose.sourceType === "git") { - command += await getCustomGitCloneCommand( - compose, - deployment.logPath, - true, - ); - console.log(command); - } else if (compose.sourceType === "raw") { - command += getCreateComposeFileCommand(compose, deployment.logPath); - } else if (compose.sourceType === "gitea") { - command += await getGiteaCloneCommand( - compose, - deployment.logPath, - true, - ); - } - - await execAsyncRemote(compose.serverId, command); - await getBuildComposeCommand(compose, deployment.logPath); - } - - await updateDeploymentStatus(deployment.deploymentId, "done"); - await updateCompose(composeId, { - composeStatus: "done", - }); - - await sendBuildSuccessNotifications({ - projectName: compose.environment.project.name, - applicationName: compose.name, - applicationType: "compose", - buildLink, - organizationId: compose.environment.project.organizationId, - domains: compose.domains, - }); - } catch (error) { - // @ts-ignore - const encodedContent = encodeBase64(error?.message); - - await execAsyncRemote( - compose.serverId, - ` - echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath}; - echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath}; - echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`, - ); - await updateDeploymentStatus(deployment.deploymentId, "error"); - await updateCompose(composeId, { - composeStatus: "error", - }); - await sendBuildErrorNotifications({ - projectName: compose.environment.project.name, - applicationName: compose.name, - applicationType: "compose", - // @ts-ignore - errorMessage: error?.message || "Error building", - buildLink, - organizationId: compose.environment.project.organizationId, - }); - throw error; - } -}; - -export const rebuildRemoteCompose = async ({ - composeId, - titleLog = "Rebuild deployment", - descriptionLog = "", -}: { - composeId: string; - titleLog: string; - descriptionLog: string; -}) => { - const compose = await findComposeById(composeId); - - const deployment = await createDeploymentCompose({ - composeId: composeId, - title: titleLog, - description: descriptionLog, - }); - - try { - if (compose.sourceType === "raw") { - const command = getCreateComposeFileCommand(compose, deployment.logPath); - await execAsyncRemote(compose.serverId, command); + await execAsyncRemote(compose.serverId, commandWithLog); + } else { + await execAsync(commandWithLog); } + command += await getBuildComposeCommand(compose); + commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; if (compose.serverId) { - await getBuildComposeCommand(compose, deployment.logPath); + await execAsyncRemote(compose.serverId, commandWithLog); + } else { + await execAsync(commandWithLog); } await updateDeploymentStatus(deployment.deploymentId, "done"); @@ -457,16 +341,6 @@ export const rebuildRemoteCompose = async ({ composeStatus: "done", }); } catch (error) { - // @ts-ignore - const encodedContent = encodeBase64(error?.message); - - await execAsyncRemote( - compose.serverId, - ` - echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath}; - echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath}; - echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`, - ); await updateDeploymentStatus(deployment.deploymentId, "error"); await updateCompose(composeId, { composeStatus: "error", diff --git a/packages/server/src/services/deployment.ts b/packages/server/src/services/deployment.ts index ed03b32fc..41274bac9 100644 --- a/packages/server/src/services/deployment.ts +++ b/packages/server/src/services/deployment.ts @@ -74,20 +74,21 @@ export const createDeployment = async ( >, ) => { const application = await findApplicationById(deployment.applicationId); - try { await removeLastTenDeployments( deployment.applicationId, "application", application.serverId, ); - const { LOGS_PATH } = paths(!!application.serverId); + const serverId = application.serverId; + + const { LOGS_PATH } = paths(!!serverId); const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss"); const fileName = `${application.appName}-${formattedDateTime}.log`; const logFilePath = path.join(LOGS_PATH, application.appName, fileName); - if (application.serverId) { - const server = await findServerById(application.serverId); + if (serverId) { + const server = await findServerById(serverId); const command = ` mkdir -p ${LOGS_PATH}/${application.appName}; @@ -99,7 +100,7 @@ export const createDeployment = async ( await fsPromises.mkdir(path.join(LOGS_PATH, application.appName), { recursive: true, }); - await fsPromises.writeFile(logFilePath, "Initializing deployment"); + await fsPromises.writeFile(logFilePath, "Initializing deployment\n"); } const deploymentCreate = await db @@ -249,7 +250,7 @@ export const createDeploymentCompose = async ( const command = ` mkdir -p ${LOGS_PATH}/${compose.appName}; -echo "Initializing deployment" >> ${logFilePath}; +echo "Initializing deployment\n" >> ${logFilePath}; `; await execAsyncRemote(server.serverId, command); @@ -257,7 +258,7 @@ echo "Initializing deployment" >> ${logFilePath}; await fsPromises.mkdir(path.join(LOGS_PATH, compose.appName), { recursive: true, }); - await fsPromises.writeFile(logFilePath, "Initializing deployment"); + await fsPromises.writeFile(logFilePath, "Initializing deployment\n"); } const deploymentCreate = await db diff --git a/packages/server/src/services/environment.ts b/packages/server/src/services/environment.ts index 1d77510be..fb1952818 100644 --- a/packages/server/src/services/environment.ts +++ b/packages/server/src/services/environment.ts @@ -34,13 +34,43 @@ export const findEnvironmentById = async (environmentId: string) => { const environment = await db.query.environments.findFirst({ where: eq(environments.environmentId, environmentId), with: { - applications: true, - mariadb: true, - mongo: true, - mysql: true, - postgres: true, - redis: true, - compose: true, + applications: { + with: { + deployments: true, + server: true, + }, + }, + mariadb: { + with: { + server: true, + }, + }, + mongo: { + with: { + server: true, + }, + }, + mysql: { + with: { + server: true, + }, + }, + postgres: { + with: { + server: true, + }, + }, + redis: { + with: { + server: true, + }, + }, + compose: { + with: { + deployments: true, + server: true, + }, + }, project: true, }, }); diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index 301573cb4..996aec352 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -59,7 +59,8 @@ export const getUpdateData = async (): Promise => { let currentDigest: string; try { currentDigest = await getServiceImageDigest(); - } catch { + } catch (error) { + console.error(error); // Docker service might not exist locally // You can run the # Installation command for docker service create mentioned in the below docs to test it locally: // https://docs.dokploy.com/docs/core/manual-installation diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 8afce6b55..cd7379429 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -1,10 +1,10 @@ import { db } from "@dokploy/server/db"; -import { apikey, member, users_temp } from "@dokploy/server/db/schema"; +import { apikey, member, user } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { and, eq } from "drizzle-orm"; import { auth } from "../lib/auth"; -export type User = typeof users_temp.$inferSelect; +export type User = typeof user.$inferSelect; export const addNewProject = async ( userId: string, @@ -403,16 +403,16 @@ export const updateUser = async (userId: string, userData: Partial) => { } } - const user = await db - .update(users_temp) + const userResult = await db + .update(user) .set({ ...userData, }) - .where(eq(users_temp.id, userId)) + .where(eq(user.id, userId)) .returning() .then((res) => res[0]); - return user; + return userResult; }; export const createApiKey = async ( diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index fa9bf78d0..73cff0b1c 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -20,7 +20,7 @@ export const TRAEFIK_PORT = Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80; export const TRAEFIK_HTTP3_PORT = Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; -export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.5.0"; +export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.6.1"; export interface TraefikOptions { env?: string[]; diff --git a/packages/server/src/templates/index.ts b/packages/server/src/templates/index.ts index 17c461510..db67cb36f 100644 --- a/packages/server/src/templates/index.ts +++ b/packages/server/src/templates/index.ts @@ -37,7 +37,16 @@ export const generateRandomDomain = ({ const hash = randomBytes(3).toString("hex"); const slugIp = serverIp.replaceAll(".", "-").replaceAll(":", "-"); - return `${projectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`; + // Domain labels have a max length of 63 characters + // Reserve space for: hash (6) + separators (1-2) + ip section + dot + traefik.me (10) + // Approx: 6 + 2 + (variable ip length) + 11 = ~19-30 chars for other parts + const maxProjectNameLength = 40; + const truncatedProjectName = + projectName.length > maxProjectNameLength + ? projectName.substring(0, maxProjectNameLength) + : projectName; + + return `${truncatedProjectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`; }; export const generateHash = (length = 8): string => { diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts index 667b46b74..7792ed11c 100644 --- a/packages/server/src/utils/builders/compose.ts +++ b/packages/server/src/utils/builders/compose.ts @@ -1,117 +1,28 @@ -import { - createWriteStream, - existsSync, - mkdirSync, - writeFileSync, -} from "node:fs"; import { dirname, join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { InferResultType } from "@dokploy/server/types/with"; import boxen from "boxen"; -import { - writeDomainsToCompose, - writeDomainsToComposeRemote, -} from "../docker/domain"; +import { writeDomainsToComposeRemote } from "../docker/domain"; import { encodeBase64, getEnviromentVariablesObject, prepareEnvironmentVariables, } from "../docker/utils"; -import { execAsync, execAsyncRemote } from "../process/execAsync"; -import { spawnAsync } from "../process/spawnAsync"; export type ComposeNested = InferResultType< "compose", { environment: { with: { project: true } }; mounts: true; domains: true } >; -export const buildCompose = async (compose: ComposeNested, logPath: string) => { - const writeStream = createWriteStream(logPath, { flags: "a" }); - const { sourceType, appName, mounts, composeType, domains } = compose; - try { - const { COMPOSE_PATH } = paths(); - const command = createCommand(compose); - await writeDomainsToCompose(compose, domains); - createEnvFile(compose); - if (compose.isolatedDeployment) { - await execAsync( - `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create ${composeType === "stack" ? "--driver overlay" : ""} --attachable ${compose.appName}`, - ); - } - - const logContent = ` - App Name: ${appName} - Build Compose 🐳 - Detected: ${mounts.length} mounts 📂 - Command: docker ${command} - Source Type: docker ${sourceType} ✅ - Compose Type: ${composeType} ✅`; - const logBox = boxen(logContent, { - padding: { - left: 1, - right: 1, - bottom: 1, - }, - width: 80, - borderStyle: "double", - }); - writeStream.write(`\n${logBox}\n`); - const projectPath = join(COMPOSE_PATH, compose.appName, "code"); - - await spawnAsync( - "docker", - [...command.split(" ")], - (data) => { - if (writeStream.writable) { - writeStream.write(data.toString()); - } - }, - { - cwd: projectPath, - env: { - NODE_ENV: process.env.NODE_ENV, - PATH: process.env.PATH, - ...(composeType === "stack" && { - ...getEnviromentVariablesObject( - compose.env, - compose.environment.project.env, - ), - }), - }, - }, - ); - - if (compose.isolatedDeployment) { - await execAsync( - `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`, - ).catch(() => {}); - } - - writeStream.write("Docker Compose Deployed: ✅"); - } catch (error) { - writeStream.write(`Error ❌ ${(error as Error).message}`); - throw error; - } finally { - writeStream.end(); - } -}; - -export const getBuildComposeCommand = async ( - compose: ComposeNested, - logPath: string, -) => { - const { COMPOSE_PATH } = paths(true); +export const getBuildComposeCommand = async (compose: ComposeNested) => { + const { COMPOSE_PATH } = paths(!!compose.serverId); const { sourceType, appName, mounts, composeType, domains } = compose; const command = createCommand(compose); const envCommand = getCreateEnvFileCommand(compose); const projectPath = join(COMPOSE_PATH, compose.appName, "code"); const exportEnvCommand = getExportEnvCommand(compose); - const newCompose = await writeDomainsToComposeRemote( - compose, - domains, - logPath, - ); + const newCompose = await writeDomainsToComposeRemote(compose, domains); const logContent = ` App Name: ${appName} Build Compose 🐳 @@ -133,7 +44,7 @@ Compose Type: ${composeType} ✅`; const bashCommand = ` set -e { - echo "${logBox}" >> "${logPath}" + echo "${logBox}"; ${newCompose} @@ -143,17 +54,18 @@ Compose Type: ${composeType} ✅`; ${exportEnvCommand} ${compose.isolatedDeployment ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` : ""} - docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; } + docker ${command.split(" ").join(" ")} 2>&1 || { echo "Error: ❌ Docker command failed"; exit 1; } ${compose.isolatedDeployment ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""} - echo "Docker Compose Deployed: ✅" >> "${logPath}" + echo "Docker Compose Deployed: ✅"; } || { - echo "Error: ❌ Script execution failed" >> "${logPath}" + echo "Error: ❌ Script execution failed"; exit 1 } `; - return await execAsyncRemote(compose.serverId, bashCommand); + return bashCommand; + // return await execAsyncRemote(compose.serverId, bashCommand); }; const sanitizeCommand = (command: string) => { @@ -185,38 +97,8 @@ export const createCommand = (compose: ComposeNested) => { return command; }; -const createEnvFile = (compose: ComposeNested) => { - const { COMPOSE_PATH } = paths(); - const { env, composePath, appName } = compose; - const composeFilePath = - join(COMPOSE_PATH, appName, "code", composePath) || - join(COMPOSE_PATH, appName, "code", "docker-compose.yml"); - - const envFilePath = join(dirname(composeFilePath), ".env"); - let envContent = `APP_NAME=${appName}\n`; - envContent += env || ""; - if (!envContent.includes("DOCKER_CONFIG")) { - envContent += "\nDOCKER_CONFIG=/root/.docker"; - } - - if (compose.randomize) { - envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`; - } - - const envFileContent = prepareEnvironmentVariables( - envContent, - compose.environment.project.env, - compose.environment.env, - ).join("\n"); - - if (!existsSync(dirname(envFilePath))) { - mkdirSync(dirname(envFilePath), { recursive: true }); - } - writeFileSync(envFilePath, envFileContent); -}; - export const getCreateEnvFileCommand = (compose: ComposeNested) => { - const { COMPOSE_PATH } = paths(true); + const { COMPOSE_PATH } = paths(!!compose.serverId); const { env, composePath, appName } = compose; const composeFilePath = join(COMPOSE_PATH, appName, "code", composePath) || diff --git a/packages/server/src/utils/builders/docker-file.ts b/packages/server/src/utils/builders/docker-file.ts index b5c2b59c3..a0acf5e6c 100644 --- a/packages/server/src/utils/builders/docker-file.ts +++ b/packages/server/src/utils/builders/docker-file.ts @@ -1,4 +1,3 @@ -import type { WriteStream } from "node:fs"; import { getEnviromentVariablesObject, prepareEnvironmentVariables, @@ -7,103 +6,10 @@ import { getBuildAppDirectory, getDockerContextPath, } from "../filesystem/directory"; -import { spawnAsync } from "../process/spawnAsync"; import type { ApplicationNested } from "."; -import { createEnvFile, createEnvFileCommand } from "./utils"; +import { createEnvFileCommand } from "./utils"; -export const buildCustomDocker = async ( - application: ApplicationNested, - writeStream: WriteStream, -) => { - const { - appName, - env, - publishDirectory, - buildArgs, - buildSecrets, - dockerBuildStage, - cleanCache, - } = application; - const dockerFilePath = getBuildAppDirectory(application); - try { - const image = `${appName}`; - - const defaultContextPath = - dockerFilePath.substring(0, dockerFilePath.lastIndexOf("/") + 1) || "."; - - const dockerContextPath = getDockerContextPath(application); - - const commandArgs = ["build", "-t", image, "-f", dockerFilePath, "."]; - - if (cleanCache) { - commandArgs.push("--no-cache"); - } - - if (dockerBuildStage) { - commandArgs.push("--target", dockerBuildStage); - } - - const args = prepareEnvironmentVariables( - buildArgs, - application.environment.project.env, - application.environment.env, - ); - - for (const arg of args) { - commandArgs.push("--build-arg", arg); - } - - const secrets = getEnviromentVariablesObject( - buildSecrets, - application.environment.project.env, - application.environment.env, - ); - - for (const key in secrets) { - // Although buildx is smart enough to know we may be referring to an environment variable name, - // we still make sure it doesn't fall back to type=file. - // See: https://docs.docker.com/reference/cli/docker/buildx/build/#secret - commandArgs.push("--secret", `type=env,id=${key}`); - } - - /* - Do not generate an environment file when publishDirectory is specified, - as it could be publicly exposed. - */ - if (!publishDirectory) { - createEnvFile( - dockerFilePath, - env, - application.environment.project.env, - application.environment.env, - ); - } - - await spawnAsync( - "docker", - commandArgs, - (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }, - { - cwd: dockerContextPath || defaultContextPath, - env: { - ...process.env, - ...secrets, - }, - }, - ); - } catch (error) { - throw error; - } -}; - -export const getDockerCommand = ( - application: ApplicationNested, - logPath: string, -) => { +export const getDockerCommand = (application: ApplicationNested) => { const { appName, env, @@ -176,17 +82,17 @@ export const getDockerCommand = ( } command += ` -echo "Building ${appName}" >> ${logPath}; -cd ${dockerContextPath} >> ${logPath} 2>> ${logPath} || { - echo "❌ The path ${dockerContextPath} does not exist" >> ${logPath}; +echo "Building ${appName}" ; +cd ${dockerContextPath} || { + echo "❌ The path ${dockerContextPath} does not exist" ; exit 1; } -${joinedSecrets} docker ${commandArgs.join(" ")} >> ${logPath} 2>> ${logPath} || { - echo "❌ Docker build failed" >> ${logPath}; +${joinedSecrets} docker ${commandArgs.join(" ")} || { + echo "❌ Docker build failed" ; exit 1; } -echo "✅ Docker build completed." >> ${logPath}; +echo "✅ Docker build completed." ; `; return command; diff --git a/packages/server/src/utils/builders/heroku.ts b/packages/server/src/utils/builders/heroku.ts index 3306f2fc2..e1ab4dff4 100644 --- a/packages/server/src/utils/builders/heroku.ts +++ b/packages/server/src/utils/builders/heroku.ts @@ -1,54 +1,8 @@ -import type { WriteStream } from "node:fs"; import { prepareEnvironmentVariables } from "../docker/utils"; import { getBuildAppDirectory } from "../filesystem/directory"; -import { spawnAsync } from "../process/spawnAsync"; import type { ApplicationNested } from "."; -// TODO: integrate in the vps sudo chown -R $(whoami) ~/.docker -export const buildHeroku = async ( - application: ApplicationNested, - writeStream: WriteStream, -) => { - const { env, appName, cleanCache } = application; - const buildAppDirectory = getBuildAppDirectory(application); - const envVariables = prepareEnvironmentVariables( - env, - application.environment.project.env, - application.environment.env, - ); - try { - const args = [ - "build", - appName, - "--path", - buildAppDirectory, - "--builder", - `heroku/builder:${application.herokuVersion || "24"}`, - ]; - - for (const env of envVariables) { - args.push("--env", env); - } - - if (cleanCache) { - args.push("--clear-cache"); - } - - await spawnAsync("pack", args, (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - return true; - } catch (e) { - throw e; - } -}; - -export const getHerokuCommand = ( - application: ApplicationNested, - logPath: string, -) => { +export const getHerokuCommand = (application: ApplicationNested) => { const { env, appName, cleanCache } = application; const buildAppDirectory = getBuildAppDirectory(application); @@ -77,12 +31,12 @@ export const getHerokuCommand = ( const command = `pack ${args.join(" ")}`; const bashCommand = ` -echo "Starting heroku build..." >> ${logPath}; -${command} >> ${logPath} 2>> ${logPath} || { - echo "❌ Heroku build failed" >> ${logPath}; +echo "Starting heroku build..." ; +${command} || { + echo "❌ Heroku build failed" ; exit 1; } -echo "✅ Heroku build completed." >> ${logPath}; +echo "✅ Heroku build completed." ; `; return bashCommand; diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index 52f8a881e..bfc3e894a 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -1,7 +1,6 @@ -import { createWriteStream } from "node:fs"; import type { InferResultType } from "@dokploy/server/types/with"; import type { CreateServiceOptions } from "dockerode"; -import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload"; +import { uploadImageRemoteCommand } from "../cluster/upload"; import { calculateResources, generateBindMounts, @@ -11,12 +10,12 @@ import { prepareEnvironmentVariables, } from "../docker/utils"; import { getRemoteDocker } from "../servers/remote-docker"; -import { buildCustomDocker, getDockerCommand } from "./docker-file"; -import { buildHeroku, getHerokuCommand } from "./heroku"; -import { buildNixpacks, getNixpacksCommand } from "./nixpacks"; -import { buildPaketo, getPaketoCommand } from "./paketo"; -import { buildRailpack, getRailpackCommand } from "./railpack"; -import { buildStatic, getStaticCommand } from "./static"; +import { getDockerCommand } from "./docker-file"; +import { getHerokuCommand } from "./heroku"; +import { getNixpacksCommand } from "./nixpacks"; +import { getPaketoCommand } from "./paketo"; +import { getRailpackCommand } from "./railpack"; +import { getStaticCommand } from "./static"; // NIXPACKS codeDirectory = where is the path of the code directory // HEROKU codeDirectory = where is the path of the code directory @@ -34,76 +33,35 @@ export type ApplicationNested = InferResultType< } >; -export const buildApplication = async ( - application: ApplicationNested, - logPath: string, -) => { - const writeStream = createWriteStream(logPath, { flags: "a" }); - const { buildType, sourceType } = application; - try { - writeStream.write( - `\nBuild ${buildType}: ✅\nSource Type: ${sourceType}: ✅\n`, - ); - console.log(`Build ${buildType}: ✅`); - if (buildType === "nixpacks") { - await buildNixpacks(application, writeStream); - } else if (buildType === "heroku_buildpacks") { - await buildHeroku(application, writeStream); - } else if (buildType === "paketo_buildpacks") { - await buildPaketo(application, writeStream); - } else if (buildType === "dockerfile") { - await buildCustomDocker(application, writeStream); - } else if (buildType === "static") { - await buildStatic(application, writeStream); - } else if (buildType === "railpack") { - await buildRailpack(application, writeStream); - } - - if (application.registryId) { - await uploadImage(application, writeStream); - } - await mechanizeDockerContainer(application); - writeStream.write("Docker Deployed: ✅"); - } catch (error) { - if (error instanceof Error) { - writeStream.write(`Error ❌\n${error?.message}`); - } else { - writeStream.write("Error ❌"); - } - throw error; - } finally { - writeStream.end(); - } -}; - -export const getBuildCommand = ( - application: ApplicationNested, - logPath: string, -) => { +export const getBuildCommand = (application: ApplicationNested) => { let command = ""; const { buildType, registry } = application; + + if (application.sourceType === "docker") { + return ""; + } switch (buildType) { case "nixpacks": - command = getNixpacksCommand(application, logPath); + command = getNixpacksCommand(application); break; case "heroku_buildpacks": - command = getHerokuCommand(application, logPath); + command = getHerokuCommand(application); break; case "paketo_buildpacks": - command = getPaketoCommand(application, logPath); + command = getPaketoCommand(application); break; case "static": - command = getStaticCommand(application, logPath); + command = getStaticCommand(application); break; case "dockerfile": - command = getDockerCommand(application, logPath); + command = getDockerCommand(application); break; case "railpack": - command = getRailpackCommand(application, logPath); + command = getRailpackCommand(application); break; } if (registry) { - command += uploadImageRemoteCommand(application, logPath); + command += uploadImageRemoteCommand(application); } return command; @@ -143,6 +101,7 @@ export const mechanizeDockerContainer = async ( UpdateConfig, Networks, StopGracePeriod, + EndpointSpec, } = generateConfigContainer(application); const bindsMount = generateBindMounts(mounts); @@ -183,14 +142,16 @@ export const mechanizeDockerContainer = async ( }, Mode, RollbackConfig, - EndpointSpec: { - Ports: ports.map((port) => ({ - PublishMode: port.publishMode, - Protocol: port.protocol, - TargetPort: port.targetPort, - PublishedPort: port.publishedPort, - })), - }, + EndpointSpec: EndpointSpec + ? EndpointSpec + : { + Ports: ports.map((port) => ({ + PublishMode: port.publishMode, + Protocol: port.protocol, + TargetPort: port.targetPort, + PublishedPort: port.publishedPort, + })), + }, UpdateConfig, ...(StopGracePeriod !== undefined && StopGracePeriod !== null && { StopGracePeriod }), diff --git a/packages/server/src/utils/builders/nixpacks.ts b/packages/server/src/utils/builders/nixpacks.ts index 76905d0e7..37f1953a4 100644 --- a/packages/server/src/utils/builders/nixpacks.ts +++ b/packages/server/src/utils/builders/nixpacks.ts @@ -1,101 +1,11 @@ -import { existsSync, mkdirSync, type WriteStream } from "node:fs"; import path from "node:path"; -import { - buildStatic, - getStaticCommand, -} from "@dokploy/server/utils/builders/static"; +import { getStaticCommand } from "@dokploy/server/utils/builders/static"; import { nanoid } from "nanoid"; import { prepareEnvironmentVariables } from "../docker/utils"; import { getBuildAppDirectory } from "../filesystem/directory"; -import { spawnAsync } from "../process/spawnAsync"; import type { ApplicationNested } from "."; -export const buildNixpacks = async ( - application: ApplicationNested, - writeStream: WriteStream, -) => { - const { env, appName, publishDirectory, cleanCache } = application; - - const buildAppDirectory = getBuildAppDirectory(application); - const buildContainerId = `${appName}-${nanoid(10)}`; - const envVariables = prepareEnvironmentVariables( - env, - application.environment.project.env, - application.environment.env, - ); - - const writeToStream = (data: string) => { - if (writeStream.writable) { - writeStream.write(data); - } - }; - - try { - const args = ["build", buildAppDirectory, "--name", appName]; - - if (cleanCache) { - args.push("--no-cache"); - } - - for (const env of envVariables) { - args.push("--env", env); - } - - if (publishDirectory) { - /* No need for any start command, since we'll use nginx later on */ - args.push("--no-error-without-start"); - } - - await spawnAsync("nixpacks", args, writeToStream); - - /* - Run the container with the image created by nixpacks, - and copy the artifacts on the host filesystem. - Then, remove the container and create a static build. - */ - if (publishDirectory) { - await spawnAsync( - "docker", - ["create", "--name", buildContainerId, appName], - writeToStream, - ); - - const localPath = path.join(buildAppDirectory, publishDirectory); - - if (!existsSync(path.dirname(localPath))) { - mkdirSync(path.dirname(localPath), { recursive: true }); - } - - // https://docs.docker.com/reference/cli/docker/container/cp/ - const isDirectory = - publishDirectory.endsWith("/") || !path.extname(publishDirectory); - - await spawnAsync( - "docker", - [ - "cp", - `${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""}`, - localPath, - ], - writeToStream, - ); - - await spawnAsync("docker", ["rm", buildContainerId], writeToStream); - - await buildStatic(application, writeStream); - } - return true; - } catch (e) { - await spawnAsync("docker", ["rm", buildContainerId], writeToStream); - - throw e; - } -}; - -export const getNixpacksCommand = ( - application: ApplicationNested, - logPath: string, -) => { +export const getNixpacksCommand = (application: ApplicationNested) => { const { env, appName, publishDirectory, cleanCache } = application; const buildAppDirectory = getBuildAppDirectory(application); @@ -122,12 +32,12 @@ export const getNixpacksCommand = ( } const command = `nixpacks ${args.join(" ")}`; let bashCommand = ` -echo "Starting nixpacks build..." >> ${logPath}; -${command} >> ${logPath} 2>> ${logPath} || { - echo "❌ Nixpacks build failed" >> ${logPath}; - exit 1; -} -echo "✅ Nixpacks build completed." >> ${logPath}; + echo "Starting nixpacks build..." ; + ${command} || { + echo "❌ Nixpacks build failed" ; + exit 1; + } + echo "✅ Nixpacks build completed." ; `; /* @@ -141,16 +51,16 @@ echo "✅ Nixpacks build completed." >> ${logPath}; publishDirectory.endsWith("/") || !path.extname(publishDirectory); bashCommand += ` -docker create --name ${buildContainerId} ${appName} -mkdir -p ${localPath} -docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} >> ${logPath} 2>> ${logPath} || { + docker create --name ${buildContainerId} ${appName} + mkdir -p ${localPath} + docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} || { + docker rm ${buildContainerId} + echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" ; + exit 1; + } docker rm ${buildContainerId} - echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" >> ${logPath}; - exit 1; -} -docker rm ${buildContainerId} -${getStaticCommand(application, logPath)} - `; + ${getStaticCommand(application)} + `; } return bashCommand; diff --git a/packages/server/src/utils/builders/paketo.ts b/packages/server/src/utils/builders/paketo.ts index b95a1bb31..eb9767e7f 100644 --- a/packages/server/src/utils/builders/paketo.ts +++ b/packages/server/src/utils/builders/paketo.ts @@ -1,53 +1,8 @@ -import type { WriteStream } from "node:fs"; import { prepareEnvironmentVariables } from "../docker/utils"; import { getBuildAppDirectory } from "../filesystem/directory"; -import { spawnAsync } from "../process/spawnAsync"; import type { ApplicationNested } from "."; -export const buildPaketo = async ( - application: ApplicationNested, - writeStream: WriteStream, -) => { - const { env, appName, cleanCache } = application; - const buildAppDirectory = getBuildAppDirectory(application); - const envVariables = prepareEnvironmentVariables( - env, - application.environment.project.env, - application.environment.env, - ); - try { - const args = [ - "build", - appName, - "--path", - buildAppDirectory, - "--builder", - "paketobuildpacks/builder-jammy-full", - ]; - - if (cleanCache) { - args.push("--clear-cache"); - } - - for (const env of envVariables) { - args.push("--env", env); - } - - await spawnAsync("pack", args, (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - return true; - } catch (e) { - throw e; - } -}; - -export const getPaketoCommand = ( - application: ApplicationNested, - logPath: string, -) => { +export const getPaketoCommand = (application: ApplicationNested) => { const { env, appName, cleanCache } = application; const buildAppDirectory = getBuildAppDirectory(application); @@ -76,12 +31,12 @@ export const getPaketoCommand = ( const command = `pack ${args.join(" ")}`; const bashCommand = ` -echo "Starting Paketo build..." >> ${logPath}; -${command} >> ${logPath} 2>> ${logPath} || { - echo "❌ Paketo build failed" >> ${logPath}; +echo "Starting Paketo build..." ; +${command} || { + echo "❌ Paketo build failed" ; exit 1; } -echo "✅ Paketo build completed." >> ${logPath}; +echo "✅ Paketo build completed." ; `; return bashCommand; diff --git a/packages/server/src/utils/builders/railpack.ts b/packages/server/src/utils/builders/railpack.ts index 4adc9ca1c..305ff20e8 100644 --- a/packages/server/src/utils/builders/railpack.ts +++ b/packages/server/src/utils/builders/railpack.ts @@ -1,13 +1,10 @@ import { createHash } from "node:crypto"; -import type { WriteStream } from "node:fs"; import { nanoid } from "nanoid"; import { parseEnvironmentKeyValuePair, prepareEnvironmentVariables, } from "../docker/utils"; import { getBuildAppDirectory } from "../filesystem/directory"; -import { execAsync } from "../process/execAsync"; -import { spawnAsync } from "../process/spawnAsync"; import type { ApplicationNested } from "."; const calculateSecretsHash = (envVariables: string[]): string => { @@ -18,108 +15,7 @@ const calculateSecretsHash = (envVariables: string[]): string => { return hash.digest("hex"); }; -export const buildRailpack = async ( - application: ApplicationNested, - writeStream: WriteStream, -) => { - const { env, appName, cleanCache } = application; - const buildAppDirectory = getBuildAppDirectory(application); - const envVariables = prepareEnvironmentVariables( - env, - application.environment.project.env, - application.environment.env, - ); - - try { - await execAsync( - "docker buildx create --use --name builder-containerd --driver docker-container || true", - ); - - await execAsync("docker buildx use builder-containerd"); - - // First prepare the build plan and info - const prepareArgs = [ - "prepare", - buildAppDirectory, - "--plan-out", - `${buildAppDirectory}/railpack-plan.json`, - "--info-out", - `${buildAppDirectory}/railpack-info.json`, - ]; - - // Add environment variables to prepare command - for (const env of envVariables) { - prepareArgs.push("--env", env); - } - - // Run prepare command - await spawnAsync("railpack", prepareArgs, (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - - // Calculate secrets hash for layer invalidation - const secretsHash = calculateSecretsHash(envVariables); - - // Build with BuildKit using the Railpack frontend - const cacheKey = cleanCache ? nanoid(10) : undefined; - const buildArgs = [ - "buildx", - "build", - ...(cacheKey - ? [ - "--build-arg", - `secrets-hash=${secretsHash}`, - "--build-arg", - `cache-key=${cacheKey}`, - ] - : []), - "--build-arg", - `BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v${application.railpackVersion}`, - "-f", - `${buildAppDirectory}/railpack-plan.json`, - "--output", - `type=docker,name=${appName}`, - ]; - - // Add secrets properly formatted - const env: { [key: string]: string } = {}; - for (const pair of envVariables) { - const [key, value] = parseEnvironmentKeyValuePair(pair); - if (key && value) { - buildArgs.push("--secret", `id=${key},env=${key}`); - env[key] = value; - } - } - - buildArgs.push(buildAppDirectory); - - await spawnAsync( - "docker", - buildArgs, - (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }, - { - env: { ...process.env, ...env }, - }, - ); - - return true; - } catch (e) { - throw e; - } finally { - await execAsync("docker buildx rm builder-containerd"); - } -}; - -export const getRailpackCommand = ( - application: ApplicationNested, - logPath: string, -) => { +export const getRailpackCommand = (application: ApplicationNested) => { const { env, appName, cleanCache } = application; const buildAppDirectory = getBuildAppDirectory(application); const envVariables = prepareEnvironmentVariables( @@ -179,25 +75,28 @@ export const getRailpackCommand = ( buildArgs.push(buildAppDirectory); const bashCommand = ` + # Ensure we have a builder with containerd docker buildx create --use --name builder-containerd --driver docker-container || true docker buildx use builder-containerd -echo "Preparing Railpack build plan..." >> "${logPath}"; -railpack ${prepareArgs.join(" ")} >> ${logPath} 2>> ${logPath} || { - echo "❌ Railpack prepare failed" >> ${logPath}; +echo "Preparing Railpack build plan..." ; +railpack ${prepareArgs.join(" ")} || { + echo "❌ Railpack prepare failed" ; + docker buildx rm builder-containerd || true exit 1; } -echo "✅ Railpack prepare completed." >> ${logPath}; +echo "✅ Railpack prepare completed." ; -echo "Building with Railpack frontend..." >> "${logPath}"; +echo "Building with Railpack frontend..." ; # Export environment variables for secrets ${exportEnvs.join("\n")} -docker ${buildArgs.join(" ")} >> ${logPath} 2>> ${logPath} || { - echo "❌ Railpack build failed" >> ${logPath}; +docker ${buildArgs.join(" ")} || { + echo "❌ Railpack build failed" ; + docker buildx rm builder-containerd || true exit 1; } -echo "✅ Railpack build completed." >> ${logPath}; +echo "✅ Railpack build completed." ; docker buildx rm builder-containerd `; diff --git a/packages/server/src/utils/builders/static.ts b/packages/server/src/utils/builders/static.ts index e59faa711..99fa25285 100644 --- a/packages/server/src/utils/builders/static.ts +++ b/packages/server/src/utils/builders/static.ts @@ -1,9 +1,5 @@ -import type { WriteStream } from "node:fs"; -import { - buildCustomDocker, - getDockerCommand, -} from "@dokploy/server/utils/builders/docker-file"; -import { createFile, getCreateFileCommand } from "../docker/utils"; +import { getDockerCommand } from "@dokploy/server/utils/builders/docker-file"; +import { getCreateFileCommand } from "../docker/utils"; import { getBuildAppDirectory } from "../filesystem/directory"; import type { ApplicationNested } from "."; @@ -32,81 +28,40 @@ http { } `; -export const buildStatic = async ( - application: ApplicationNested, - writeStream: WriteStream, -) => { +export const getStaticCommand = (application: ApplicationNested) => { const { publishDirectory, isStaticSpa } = application; const buildAppDirectory = getBuildAppDirectory(application); - - try { - if (isStaticSpa) { - createFile(buildAppDirectory, "nginx.conf", nginxSpaConfig); - } - - createFile( + let command = ""; + if (isStaticSpa) { + command += getCreateFileCommand( buildAppDirectory, - ".dockerignore", - [".git", ".env", "Dockerfile", ".dockerignore"].join("\n"), + "nginx.conf", + nginxSpaConfig, ); - - createFile( - buildAppDirectory, - "Dockerfile", - [ - "FROM nginx:alpine", - "WORKDIR /usr/share/nginx/html/", - isStaticSpa ? "COPY nginx.conf /etc/nginx/nginx.conf" : "", - `COPY ${publishDirectory || "."} .`, - 'CMD ["nginx", "-g", "daemon off;"]', - ].join("\n"), - ); - - createFile( - buildAppDirectory, - ".dockerignore", - [".git", ".env", "Dockerfile", ".dockerignore"].join("\n"), - ); - - await buildCustomDocker( - { - ...application, - buildType: "dockerfile", - dockerfile: "Dockerfile", - }, - writeStream, - ); - - return true; - } catch (e) { - throw e; } -}; -export const getStaticCommand = ( - application: ApplicationNested, - logPath: string, -) => { - const { publishDirectory } = application; - const buildAppDirectory = getBuildAppDirectory(application); + command += getCreateFileCommand( + buildAppDirectory, + ".dockerignore", + [".git", ".env", "Dockerfile", ".dockerignore"].join("\n"), + ); - let command = getCreateFileCommand( + command += getCreateFileCommand( buildAppDirectory, "Dockerfile", [ "FROM nginx:alpine", "WORKDIR /usr/share/nginx/html/", + isStaticSpa ? "COPY nginx.conf /etc/nginx/nginx.conf" : "", `COPY ${publishDirectory || "."} .`, + 'CMD ["nginx", "-g", "daemon off;"]', ].join("\n"), ); - command += getDockerCommand( - { - ...application, - buildType: "dockerfile", - dockerfile: "Dockerfile", - }, - logPath, - ); + command += getDockerCommand({ + ...application, + buildType: "dockerfile", + dockerfile: "Dockerfile", + }); return command; }; diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts index c13a2701c..33be287e1 100644 --- a/packages/server/src/utils/cluster/upload.ts +++ b/packages/server/src/utils/cluster/upload.ts @@ -1,11 +1,6 @@ -import type { WriteStream } from "node:fs"; import type { ApplicationNested } from "../builders"; -import { spawnAsync } from "../process/spawnAsync"; -export const uploadImage = async ( - application: ApplicationNested, - writeStream: WriteStream, -) => { +export const uploadImageRemoteCommand = (application: ApplicationNested) => { const registry = application.registry; if (!registry) { @@ -19,85 +14,28 @@ export const uploadImage = async ( const finalURL = registryUrl; // Build registry tag in correct format: registry.com/owner/image:tag - // For ghcr.io: ghcr.io/username/image:tag - // For docker.io: docker.io/username/image:tag const registryTag = imagePrefix ? `${registryUrl ? `${registryUrl}/` : ""}${imagePrefix}/${imageName}` : `${registryUrl ? `${registryUrl}/` : ""}${username}/${imageName}`; - try { - writeStream.write( - `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL} | ${registryTag}\n`, - ); - const loginCommand = spawnAsync( - "docker", - ["login", finalURL, "-u", registry.username, "--password-stdin"], - (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }, - ); - loginCommand.child?.stdin?.write(registry.password); - loginCommand.child?.stdin?.end(); - await loginCommand; - - await spawnAsync("docker", ["tag", imageName, registryTag], (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - - await spawnAsync("docker", ["push", registryTag], (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - } catch (error) { - console.log(error); - throw error; - } -}; - -export const uploadImageRemoteCommand = ( - application: ApplicationNested, - logPath: string, -) => { - const registry = application.registry; - - if (!registry) { - throw new Error("Registry not found"); - } - - const { registryUrl, imagePrefix, username } = registry; - const { appName } = application; - const imageName = `${appName}:latest`; - - const finalURL = registryUrl; - - // Build registry tag in correct format: registry.com/owner/image:tag - const registryTag = imagePrefix - ? `${registryUrl}/${imagePrefix}/${imageName}` - : `${registryUrl}/${username}/${imageName}`; - try { const command = ` - echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" >> ${logPath}; - echo "${registry.password}" | docker login ${finalURL} -u ${registry.username} --password-stdin >> ${logPath} 2>> ${logPath} || { - echo "❌ DockerHub Failed" >> ${logPath}; + echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ; + echo "${registry.password}" | docker login ${finalURL} -u ${registry.username} --password-stdin || { + echo "❌ DockerHub Failed" ; exit 1; } - echo "✅ Registry Login Success" >> ${logPath}; - docker tag ${imageName} ${registryTag} >> ${logPath} 2>> ${logPath} || { - echo "❌ Error tagging image" >> ${logPath}; + echo "✅ Registry Login Success" ; + docker tag ${imageName} ${registryTag} || { + echo "❌ Error tagging image" ; exit 1; } - echo "✅ Image Tagged" >> ${logPath}; - docker push ${registryTag} 2>> ${logPath} || { - echo "❌ Error pushing image" >> ${logPath}; + echo "✅ Image Tagged" ; + docker push ${registryTag} || { + echo "❌ Error pushing image" ; exit 1; } - echo "✅ Image Pushed" >> ${logPath}; + echo "✅ Image Pushed" ; `; return command; } catch (error) { diff --git a/packages/server/src/utils/databases/mariadb.ts b/packages/server/src/utils/databases/mariadb.ts index 196a394bc..d06e808e5 100644 --- a/packages/server/src/utils/databases/mariadb.ts +++ b/packages/server/src/utils/databases/mariadb.ts @@ -46,6 +46,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => { UpdateConfig, Networks, StopGracePeriod, + EndpointSpec, } = generateConfigContainer(mariadb); const resources = calculateResources({ memoryLimit, @@ -89,19 +90,21 @@ export const buildMariadb = async (mariadb: MariadbNested) => { }, Mode, RollbackConfig, - EndpointSpec: { - Mode: "dnsrr", - Ports: externalPort - ? [ - { - Protocol: "tcp", - TargetPort: 3306, - PublishedPort: externalPort, - PublishMode: "host", - }, - ] - : [], - }, + EndpointSpec: EndpointSpec + ? EndpointSpec + : { + Mode: "dnsrr" as const, + Ports: externalPort + ? [ + { + Protocol: "tcp" as const, + TargetPort: 3306, + PublishedPort: externalPort, + PublishMode: "host" as const, + }, + ] + : [], + }, UpdateConfig, ...(StopGracePeriod !== undefined && StopGracePeriod !== null && { StopGracePeriod }), diff --git a/packages/server/src/utils/databases/mongo.ts b/packages/server/src/utils/databases/mongo.ts index 000049cf8..8fef4f054 100644 --- a/packages/server/src/utils/databases/mongo.ts +++ b/packages/server/src/utils/databases/mongo.ts @@ -92,6 +92,7 @@ ${command ?? "wait $MONGOD_PID"}`; UpdateConfig, Networks, StopGracePeriod, + EndpointSpec, } = generateConfigContainer(mongo); const resources = calculateResources({ @@ -142,19 +143,21 @@ ${command ?? "wait $MONGOD_PID"}`; }, Mode, RollbackConfig, - EndpointSpec: { - Mode: "dnsrr", - Ports: externalPort - ? [ - { - Protocol: "tcp", - TargetPort: 27017, - PublishedPort: externalPort, - PublishMode: "host", - }, - ] - : [], - }, + EndpointSpec: EndpointSpec + ? EndpointSpec + : { + Mode: "dnsrr" as const, + Ports: externalPort + ? [ + { + Protocol: "tcp" as const, + TargetPort: 27017, + PublishedPort: externalPort, + PublishMode: "host" as const, + }, + ] + : [], + }, UpdateConfig, ...(StopGracePeriod !== undefined && StopGracePeriod !== null && { StopGracePeriod }), diff --git a/packages/server/src/utils/databases/mysql.ts b/packages/server/src/utils/databases/mysql.ts index 2956b52e5..d1ed8318c 100644 --- a/packages/server/src/utils/databases/mysql.ts +++ b/packages/server/src/utils/databases/mysql.ts @@ -52,6 +52,7 @@ export const buildMysql = async (mysql: MysqlNested) => { UpdateConfig, Networks, StopGracePeriod, + EndpointSpec, } = generateConfigContainer(mysql); const resources = calculateResources({ memoryLimit, @@ -95,19 +96,21 @@ export const buildMysql = async (mysql: MysqlNested) => { }, Mode, RollbackConfig, - EndpointSpec: { - Mode: "dnsrr", - Ports: externalPort - ? [ - { - Protocol: "tcp", - TargetPort: 3306, - PublishedPort: externalPort, - PublishMode: "host", - }, - ] - : [], - }, + EndpointSpec: EndpointSpec + ? EndpointSpec + : { + Mode: "dnsrr" as const, + Ports: externalPort + ? [ + { + Protocol: "tcp" as const, + TargetPort: 3306, + PublishedPort: externalPort, + PublishMode: "host" as const, + }, + ] + : [], + }, UpdateConfig, ...(StopGracePeriod !== undefined && StopGracePeriod !== null && { StopGracePeriod }), diff --git a/packages/server/src/utils/databases/postgres.ts b/packages/server/src/utils/databases/postgres.ts index 1b0d62c0a..85bc957ae 100644 --- a/packages/server/src/utils/databases/postgres.ts +++ b/packages/server/src/utils/databases/postgres.ts @@ -45,6 +45,7 @@ export const buildPostgres = async (postgres: PostgresNested) => { UpdateConfig, Networks, StopGracePeriod, + EndpointSpec, } = generateConfigContainer(postgres); const resources = calculateResources({ memoryLimit, @@ -88,19 +89,21 @@ export const buildPostgres = async (postgres: PostgresNested) => { }, Mode, RollbackConfig, - EndpointSpec: { - Mode: "dnsrr", - Ports: externalPort - ? [ - { - Protocol: "tcp", - TargetPort: 5432, - PublishedPort: externalPort, - PublishMode: "host", - }, - ] - : [], - }, + EndpointSpec: EndpointSpec + ? EndpointSpec + : { + Mode: "dnsrr" as const, + Ports: externalPort + ? [ + { + Protocol: "tcp" as const, + TargetPort: 5432, + PublishedPort: externalPort, + PublishMode: "host" as const, + }, + ] + : [], + }, UpdateConfig, ...(StopGracePeriod !== undefined && StopGracePeriod !== null && { StopGracePeriod }), diff --git a/packages/server/src/utils/databases/redis.ts b/packages/server/src/utils/databases/redis.ts index 98c3ba62a..35e854058 100644 --- a/packages/server/src/utils/databases/redis.ts +++ b/packages/server/src/utils/databases/redis.ts @@ -43,6 +43,7 @@ export const buildRedis = async (redis: RedisNested) => { UpdateConfig, Networks, StopGracePeriod, + EndpointSpec, } = generateConfigContainer(redis); const resources = calculateResources({ memoryLimit, @@ -85,19 +86,21 @@ export const buildRedis = async (redis: RedisNested) => { }, Mode, RollbackConfig, - EndpointSpec: { - Mode: "dnsrr", - Ports: externalPort - ? [ - { - Protocol: "tcp", - TargetPort: 6379, - PublishedPort: externalPort, - PublishMode: "host", - }, - ] - : [], - }, + EndpointSpec: EndpointSpec + ? EndpointSpec + : { + Mode: "dnsrr" as const, + Ports: externalPort + ? [ + { + Protocol: "tcp" as const, + TargetPort: 6379, + PublishedPort: externalPort, + PublishMode: "host" as const, + }, + ] + : [], + }, UpdateConfig, ...(StopGracePeriod !== undefined && StopGracePeriod !== null && { StopGracePeriod }), diff --git a/packages/server/src/utils/docker/collision.ts b/packages/server/src/utils/docker/collision.ts index 9752100ca..88d20d4d8 100644 --- a/packages/server/src/utils/docker/collision.ts +++ b/packages/server/src/utils/docker/collision.ts @@ -1,11 +1,11 @@ import { findComposeById } from "@dokploy/server/services/compose"; import { stringify } from "yaml"; +import { execAsync, execAsyncRemote } from "../process/execAsync"; import { addAppNameToAllServiceNames } from "./collision/root-network"; import { generateRandomHash } from "./compose"; import { addSuffixToAllVolumes } from "./compose/volume"; import { cloneCompose, - cloneComposeRemote, loadDockerCompose, loadDockerComposeRemote, } from "./domain"; @@ -31,10 +31,11 @@ export const randomizeIsolatedDeploymentComposeFile = async ( ) => { const compose = await findComposeById(composeId); + const command = await cloneCompose(compose); if (compose.serverId) { - await cloneComposeRemote(compose); + await execAsyncRemote(compose.serverId, command); } else { - await cloneCompose(compose); + await execAsync(command); } let composeData: ComposeSpecification | null; diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts index 7a9521d1d..a176a4560 100644 --- a/packages/server/src/utils/docker/domain.ts +++ b/packages/server/src/utils/docker/domain.ts @@ -1,35 +1,16 @@ import fs, { existsSync, readFileSync } from "node:fs"; -import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { Compose } from "@dokploy/server/services/compose"; import type { Domain } from "@dokploy/server/services/domain"; import { parse, stringify } from "yaml"; import { execAsyncRemote } from "../process/execAsync"; -import { - cloneRawBitbucketRepository, - cloneRawBitbucketRepositoryRemote, -} from "../providers/bitbucket"; -import { - cloneGitRawRepository, - cloneRawGitRepositoryRemote, -} from "../providers/git"; -import { - cloneRawGiteaRepository, - cloneRawGiteaRepositoryRemote, -} from "../providers/gitea"; -import { - cloneRawGithubRepository, - cloneRawGithubRepositoryRemote, -} from "../providers/github"; -import { - cloneRawGitlabRepository, - cloneRawGitlabRepositoryRemote, -} from "../providers/gitlab"; -import { - createComposeFileRaw, - createComposeFileRawRemote, -} from "../providers/raw"; +import { cloneBitbucketRepository } from "../providers/bitbucket"; +import { cloneGitRepository } from "../providers/git"; +import { cloneGiteaRepository } from "../providers/gitea"; +import { cloneGithubRepository } from "../providers/github"; +import { cloneGitlabRepository } from "../providers/gitlab"; +import { getCreateComposeFileCommand } from "../providers/raw"; import { randomizeDeployableSpecificationFile } from "./collision"; import { randomizeSpecificationFile } from "./compose"; import type { @@ -40,35 +21,25 @@ import type { import { encodeBase64 } from "./utils"; export const cloneCompose = async (compose: Compose) => { + let command = "set -e;"; + const entity = { + ...compose, + type: "compose" as const, + }; if (compose.sourceType === "github") { - await cloneRawGithubRepository(compose); + command += await cloneGithubRepository(entity); } else if (compose.sourceType === "gitlab") { - await cloneRawGitlabRepository(compose); + command += await cloneGitlabRepository(entity); } else if (compose.sourceType === "bitbucket") { - await cloneRawBitbucketRepository(compose); + command += await cloneBitbucketRepository(entity); } else if (compose.sourceType === "git") { - await cloneGitRawRepository(compose); + command += await cloneGitRepository(entity); } else if (compose.sourceType === "gitea") { - await cloneRawGiteaRepository(compose); + command += await cloneGiteaRepository(entity); } else if (compose.sourceType === "raw") { - await createComposeFileRaw(compose); - } -}; - -export const cloneComposeRemote = async (compose: Compose) => { - if (compose.sourceType === "github") { - await cloneRawGithubRepositoryRemote(compose); - } else if (compose.sourceType === "gitlab") { - await cloneRawGitlabRepositoryRemote(compose); - } else if (compose.sourceType === "bitbucket") { - await cloneRawBitbucketRepositoryRemote(compose); - } else if (compose.sourceType === "git") { - await cloneRawGitRepositoryRemote(compose); - } else if (compose.sourceType === "gitea") { - await cloneRawGiteaRepositoryRemote(compose); - } else if (compose.sourceType === "raw") { - await createComposeFileRawRemote(compose); + command += getCreateComposeFileCommand(compose); } + return command; }; export const getComposePath = (compose: Compose) => { @@ -131,28 +102,9 @@ export const readComposeFile = async (compose: Compose) => { 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 = stringify(composeConverted, { lineWidth: 1000 }); - try { - await writeFile(path, composeString, "utf8"); - } catch (error) { - throw error; - } -}; - export const writeDomainsToComposeRemote = async ( compose: Compose, domains: Domain[], - logPath: string, ) => { if (!domains.length) { return ""; @@ -164,7 +116,7 @@ export const writeDomainsToComposeRemote = async ( if (!composeConverted) { return ` -echo "❌ Error: Compose file not found" >> ${logPath}; +echo "❌ Error: Compose file not found"; exit 1; `; } @@ -175,12 +127,13 @@ exit 1; } } catch (error) { // @ts-ignore - return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath}; + return `echo "❌ Has occured an error: ${error?.message || error}"; exit 1; `; } + + return ""; }; -// (node:59875) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit export const addDomainToCompose = async ( compose: Compose, domains: Domain[], @@ -190,7 +143,7 @@ export const addDomainToCompose = async ( let result: ComposeSpecification | null; if (compose.serverId) { - result = await loadDockerComposeRemote(compose); // aca hay que ir al servidor e ir a traer el compose file al servidor + result = await loadDockerComposeRemote(compose); } else { result = await loadDockerCompose(compose); } diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index 55a38bd96..6d00aa0df 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -395,6 +395,7 @@ export const generateConfigContainer = ( mounts, networkSwarm, stopGracePeriodSwarm, + endpointSpecSwarm, } = application; const sanitizedStopGracePeriodSwarm = @@ -408,11 +409,9 @@ export const generateConfigContainer = ( ...(healthCheckSwarm && { HealthCheck: healthCheckSwarm, }), - ...(restartPolicySwarm - ? { - RestartPolicy: restartPolicySwarm, - } - : {}), + ...(restartPolicySwarm && { + RestartPolicy: restartPolicySwarm, + }), ...(placementSwarm ? { Placement: placementSwarm, @@ -461,6 +460,18 @@ export const generateConfigContainer = ( : { Networks: [{ Target: "dokploy-network" }], }), + ...(endpointSpecSwarm && { + EndpointSpec: { + ...(endpointSpecSwarm.Mode && { Mode: endpointSpecSwarm.Mode }), + Ports: + endpointSpecSwarm.Ports?.map((port) => ({ + Protocol: (port.Protocol || "tcp") as "tcp" | "udp" | "sctp", + TargetPort: port.TargetPort || 0, + PublishedPort: port.PublishedPort || 0, + PublishMode: (port.PublishMode || "host") as "ingress" | "host", + })) || [], + }, + }), }; }; diff --git a/packages/server/src/utils/process/execAsync.ts b/packages/server/src/utils/process/execAsync.ts index 84f0701d9..13b06c6c4 100644 --- a/packages/server/src/utils/process/execAsync.ts +++ b/packages/server/src/utils/process/execAsync.ts @@ -116,11 +116,7 @@ export const execAsyncRemote = async ( if (code === 0) { resolve({ stdout, stderr }); } else { - reject( - new Error( - `Command exited with code ${code}. Stderr: ${stderr}, command: ${command}`, - ), - ); + reject(new Error(`Error occurred ❌: ${stderr}`)); } }) .on("data", (data: string) => { diff --git a/packages/server/src/utils/providers/bitbucket.ts b/packages/server/src/utils/providers/bitbucket.ts index ed6cd8c31..2248baaaf 100644 --- a/packages/server/src/utils/providers/bitbucket.ts +++ b/packages/server/src/utils/providers/bitbucket.ts @@ -1,4 +1,3 @@ -import { createWriteStream } from "node:fs"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { @@ -9,12 +8,8 @@ import { type Bitbucket, findBitbucketById, } from "@dokploy/server/services/bitbucket"; -import type { Compose } from "@dokploy/server/services/compose"; import type { InferResultType } from "@dokploy/server/types/with"; import { TRPCError } from "@trpc/server"; -import { recreateDirectory } from "../filesystem/directory"; -import { execAsyncRemote } from "../process/execAsync"; -import { spawnAsync } from "../process/spawnAsync"; export type ApplicationWithBitbucket = InferResultType< "applications", @@ -81,202 +76,52 @@ export const getBitbucketHeaders = (bitbucketProvider: Bitbucket) => { }; }; -export const cloneBitbucketRepository = async ( - entity: ApplicationWithBitbucket | ComposeWithBitbucket, - logPath: string, - isCompose = false, -) => { - const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(); - const writeStream = createWriteStream(logPath, { flags: "a" }); +interface CloneBitbucketRepository { + appName: string; + bitbucketRepository: string | null; + bitbucketOwner: string | null; + bitbucketBranch: string | null; + bitbucketId: string | null; + enableSubmodules: boolean; + serverId: string | null; + type?: "application" | "compose"; +} + +export const cloneBitbucketRepository = async ({ + type = "application", + ...entity +}: CloneBitbucketRepository) => { + let command = "set -e;"; const { appName, bitbucketRepository, bitbucketOwner, bitbucketBranch, bitbucketId, - bitbucket, enableSubmodules, + serverId, } = entity; + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(!!serverId); if (!bitbucketId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Bitbucket Provider not found", - }); + command += `echo "Error: ❌ Bitbucket Provider not found"; exit 1;`; + return command; } + const bitbucket = await findBitbucketById(bitbucketId); - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; + if (!bitbucket) { + command += `echo "Error: ❌ Bitbucket Provider not found"; exit 1;`; + return command; + } + const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); + command += `rm -rf ${outputPath};`; + command += `mkdir -p ${outputPath};`; const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`; const cloneUrl = getBitbucketCloneUrl(bitbucket, repoclone); - try { - writeStream.write(`\nCloning Repo ${repoclone} to ${outputPath}: ✅\n`); - const cloneArgs = [ - "clone", - "--branch", - bitbucketBranch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ]; - - await spawnAsync("git", cloneArgs, (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - writeStream.write(`\nCloned ${repoclone} to ${outputPath}: ✅\n`); - } catch (error) { - writeStream.write(`ERROR Cloning: ${error}: ❌`); - throw error; - } finally { - writeStream.end(); - } -}; - -export const cloneRawBitbucketRepository = async (entity: Compose) => { - const { COMPOSE_PATH } = paths(); - const { - appName, - bitbucketRepository, - bitbucketOwner, - bitbucketBranch, - bitbucketId, - enableSubmodules, - } = entity; - - if (!bitbucketId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Bitbucket Provider not found", - }); - } - - const bitbucketProvider = await findBitbucketById(bitbucketId); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); - const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`; - const cloneUrl = getBitbucketCloneUrl(bitbucketProvider, repoclone); - - try { - const cloneArgs = [ - "clone", - "--branch", - bitbucketBranch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ]; - - await spawnAsync("git", cloneArgs); - } catch (error) { - throw error; - } -}; - -export const cloneRawBitbucketRepositoryRemote = async (compose: Compose) => { - const { COMPOSE_PATH } = paths(true); - const { - appName, - bitbucketRepository, - bitbucketOwner, - bitbucketBranch, - bitbucketId, - serverId, - enableSubmodules, - } = compose; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - if (!bitbucketId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Bitbucket Provider not found", - }); - } - - const bitbucketProvider = await findBitbucketById(bitbucketId); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`; - const cloneUrl = getBitbucketCloneUrl(bitbucketProvider, repoclone); - - try { - const cloneCommand = ` - rm -rf ${outputPath}; - git clone --branch ${bitbucketBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} - `; - await execAsyncRemote(serverId, cloneCommand); - } catch (error) { - throw error; - } -}; - -export const getBitbucketCloneCommand = async ( - entity: ApplicationWithBitbucket | ComposeWithBitbucket, - logPath: string, - isCompose = false, -) => { - const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); - const { - appName, - bitbucketRepository, - bitbucketOwner, - bitbucketBranch, - bitbucketId, - serverId, - enableSubmodules, - } = entity; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - - if (!bitbucketId) { - const command = ` - echo "Error: ❌ Bitbucket Provider not found" >> ${logPath}; - exit 1; - `; - await execAsyncRemote(serverId, command); - throw new TRPCError({ - code: "NOT_FOUND", - message: "Bitbucket Provider not found", - }); - } - - const bitbucketProvider = await findBitbucketById(bitbucketId); - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; - const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); - const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`; - const cloneUrl = getBitbucketCloneUrl(bitbucketProvider, repoclone); - - const cloneCommand = ` -rm -rf ${outputPath}; -mkdir -p ${outputPath}; -if ! git clone --branch ${bitbucketBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then - echo "❌ [ERROR] Fail to clone the repository ${repoclone}" >> ${logPath}; - exit 1; -fi -echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath}; - `; - - return cloneCommand; + command += `echo "Cloning Repo ${repoclone} to ${outputPath}: ✅";`; + command += `git clone --branch ${bitbucketBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`; + return command; }; export const getBitbucketRepositories = async (bitbucketId?: string) => { diff --git a/packages/server/src/utils/providers/docker.ts b/packages/server/src/utils/providers/docker.ts index 56341b7d6..06f962dc7 100644 --- a/packages/server/src/utils/providers/docker.ts +++ b/packages/server/src/utils/providers/docker.ts @@ -1,60 +1,6 @@ -import { createWriteStream } from "node:fs"; -import { type ApplicationNested, mechanizeDockerContainer } from "../builders"; -import { pullImage } from "../docker/utils"; +import type { ApplicationNested } from "../builders"; -interface RegistryAuth { - username: string; - password: string; - registryUrl: string; -} - -export const buildDocker = async ( - application: ApplicationNested, - logPath: string, -): Promise => { - const { buildType, dockerImage, username, password } = application; - const authConfig: Partial = { - username: username || "", - password: password || "", - registryUrl: application.registryUrl || "", - }; - - const writeStream = createWriteStream(logPath, { flags: "a" }); - - writeStream.write(`\nBuild ${buildType}\n`); - - writeStream.write(`Pulling ${dockerImage}: ✅\n`); - - try { - if (!dockerImage) { - throw new Error("Docker image not found"); - } - - await pullImage( - dockerImage, - (data) => { - if (writeStream.writable) { - writeStream.write(`${data}\n`); - } - }, - authConfig, - ); - await mechanizeDockerContainer(application); - writeStream.write("\nDocker Deployed: ✅\n"); - } catch (error) { - writeStream.write( - `❌ Error: ${error instanceof Error ? error.message : String(error)}`, - ); - throw error; - } finally { - writeStream.end(); - } -}; - -export const buildRemoteDocker = async ( - application: ApplicationNested, - logPath: string, -) => { +export const buildRemoteDocker = async (application: ApplicationNested) => { const { registryUrl, dockerImage, username, password } = application; try { @@ -62,25 +8,25 @@ export const buildRemoteDocker = async ( throw new Error("Docker image not found"); } let command = ` -echo "Pulling ${dockerImage}" >> ${logPath}; +echo "Pulling ${dockerImage}"; `; if (username && password) { command += ` -if ! echo "${password}" | docker login --username "${username}" --password-stdin "${registryUrl || ""}" >> ${logPath} 2>&1; then - echo "❌ Login failed" >> ${logPath}; +if ! echo "${password}" | docker login --username "${username}" --password-stdin "${registryUrl || ""}" 2>&1; then + echo "❌ Login failed"; exit 1; fi `; } command += ` -docker pull ${dockerImage} >> ${logPath} 2>> ${logPath} || { - echo "❌ Pulling image failed" >> ${logPath}; +docker pull ${dockerImage} 2>&1 || { + echo "❌ Pulling image failed"; exit 1; } -echo "✅ Pulling image completed." >> ${logPath}; +echo "✅ Pulling image completed."; `; return command; } catch (error) { diff --git a/packages/server/src/utils/providers/git.ts b/packages/server/src/utils/providers/git.ts index 5779252db..8e640892d 100644 --- a/packages/server/src/utils/providers/git.ts +++ b/packages/server/src/utils/providers/git.ts @@ -1,159 +1,65 @@ -import { createWriteStream } from "node:fs"; import path, { join } from "node:path"; import { paths } from "@dokploy/server/constants"; -import type { Compose } from "@dokploy/server/services/compose"; import { findSSHKeyById, updateSSHKeyById, } from "@dokploy/server/services/ssh-key"; -import { TRPCError } from "@trpc/server"; -import { recreateDirectory } from "../filesystem/directory"; import { execAsync, execAsyncRemote } from "../process/execAsync"; -import { spawnAsync } from "../process/spawnAsync"; -export const cloneGitRepository = async ( - entity: { - appName: string; - customGitUrl?: string | null; - customGitBranch?: string | null; - customGitSSHKeyId?: string | null; - enableSubmodules?: boolean; - }, - logPath: string, - isCompose = false, -) => { - const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(); +interface CloneGitRepository { + appName: string; + customGitUrl?: string | null; + customGitBranch?: string | null; + customGitSSHKeyId?: string | null; + enableSubmodules?: boolean; + serverId: string | null; + type?: "application" | "compose"; +} + +export const cloneGitRepository = async ({ + type = "application", + ...entity +}: CloneGitRepository) => { + let command = "set -e;"; const { appName, customGitUrl, customGitBranch, customGitSSHKeyId, enableSubmodules, + serverId, } = entity; + const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(!!serverId); if (!customGitUrl || !customGitBranch) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error: Repository not found", - }); + command += `echo "Error: ❌ Repository not found"; exit 1;`; + return command; } - const writeStream = createWriteStream(logPath, { flags: "a" }); const temporalKeyPath = path.join("/tmp", "id_rsa"); if (customGitSSHKeyId) { const sshKey = await findSSHKeyById(customGitSSHKeyId); - await execAsync(` + command += ` echo "${sshKey.privateKey}" > ${temporalKeyPath} - chmod 600 ${temporalKeyPath} - `); + chmod 600 ${temporalKeyPath}; + `; } - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; + const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); const knownHostsPath = path.join(SSH_PATH, "known_hosts"); - try { - if (!isHttpOrHttps(customGitUrl)) { - if (!customGitSSHKeyId) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key", - }); - } - await addHostToKnownHosts(customGitUrl); + if (!isHttpOrHttps(customGitUrl)) { + if (!customGitSSHKeyId) { + command += `echo "Error: ❌ You are trying to clone a ssh repository without a ssh key, please set a ssh key"; exit 1;`; + return command; } - await recreateDirectory(outputPath); - writeStream.write( - `\nCloning Repo Custom ${customGitUrl} to ${outputPath}: ✅\n`, - ); - - if (customGitSSHKeyId) { - await updateSSHKeyById({ - sshKeyId: customGitSSHKeyId, - lastUsedAt: new Date().toISOString(), - }); - } - - const { port } = sanitizeRepoPathSSH(customGitUrl); - const cloneArgs = [ - "clone", - "--branch", - customGitBranch, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - customGitUrl, - outputPath, - "--progress", - ]; - - await spawnAsync( - "git", - cloneArgs, - (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }, - { - env: { - ...process.env, - ...(customGitSSHKeyId && { - GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`, - }), - }, - }, - ); - - writeStream.write(`\nCloned Custom Git ${customGitUrl}: ✅\n`); - } catch (error) { - writeStream.write(`\nERROR Cloning Custom Git: ${error}: ❌\n`); - throw error; - } finally { - writeStream.end(); + command += addHostToKnownHostsCommand(customGitUrl); } -}; - -export const getCustomGitCloneCommand = async ( - entity: { - appName: string; - customGitUrl?: string | null; - customGitBranch?: string | null; - customGitSSHKeyId?: string | null; - serverId: string | null; - enableSubmodules: boolean; - }, - logPath: string, - isCompose = false, -) => { - const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); - const { - appName, - customGitUrl, - customGitBranch, - customGitSSHKeyId, - serverId, - enableSubmodules, - } = entity; - - if (!customGitUrl || !customGitBranch) { - const command = ` - echo "Error: ❌ Repository not found" >> ${logPath}; - exit 1; - `; - - await execAsyncRemote(serverId, command); - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error: Repository not found", - }); - } - - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; - const outputPath = join(basePath, appName, "code"); - const knownHostsPath = path.join(SSH_PATH, "known_hosts"); + command += `rm -rf ${outputPath};`; + command += `mkdir -p ${outputPath};`; + command += `echo "Cloning Repo Custom ${customGitUrl} to ${outputPath}: ✅";`; if (customGitSSHKeyId) { await updateSSHKeyById({ @@ -161,48 +67,22 @@ export const getCustomGitCloneCommand = async ( lastUsedAt: new Date().toISOString(), }); } - try { - const command = []; - if (!isHttpOrHttps(customGitUrl)) { - if (!customGitSSHKeyId) { - command.push( - `echo "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key ❌" >> ${logPath}; - exit 1; - `, - ); - } - command.push(addHostToKnownHostsCommand(customGitUrl)); - } - command.push(`rm -rf ${outputPath};`); - command.push(`mkdir -p ${outputPath};`); - command.push( - `echo "Cloning Custom Git ${customGitUrl}" to ${outputPath}: ✅ >> ${logPath};`, - ); - if (customGitSSHKeyId) { - const sshKey = await findSSHKeyById(customGitSSHKeyId); - const { port } = sanitizeRepoPathSSH(customGitUrl); - const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`; - command.push( - ` - echo "${sshKey.privateKey}" > /tmp/id_rsa - chmod 600 /tmp/id_rsa - export GIT_SSH_COMMAND="${gitSshCommand}" - `, - ); - } - command.push( - `if ! git clone --branch ${customGitBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${customGitUrl} ${outputPath} >> ${logPath} 2>&1; then - echo "❌ [ERROR] Fail to clone the repository ${customGitUrl}" >> ${logPath}; + if (customGitSSHKeyId) { + const sshKey = await findSSHKeyById(customGitSSHKeyId); + const { port } = sanitizeRepoPathSSH(customGitUrl); + const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`; + command += `echo "${sshKey.privateKey}" > /tmp/id_rsa;`; + command += "chmod 600 /tmp/id_rsa;"; + command += `export GIT_SSH_COMMAND="${gitSshCommand}";`; + } + command += `if ! git clone --branch ${customGitBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${customGitUrl} ${outputPath}; then + echo "❌ [ERROR] Fail to clone the repository ${customGitUrl}"; exit 1; fi - `, - ); - command.push(`echo "Cloned Custom Git ${customGitUrl}: ✅" >> ${logPath};`); - return command.join("\n"); - } catch (error) { - throw error; - } + `; + + return command; }; const isHttpOrHttps = (url: string): boolean => { @@ -210,19 +90,19 @@ const isHttpOrHttps = (url: string): boolean => { return regex.test(url); }; -const addHostToKnownHosts = async (repositoryURL: string) => { - const { SSH_PATH } = paths(); - const { domain, port } = sanitizeRepoPathSSH(repositoryURL); - const knownHostsPath = path.join(SSH_PATH, "known_hosts"); +// const addHostToKnownHosts = async (repositoryURL: string) => { +// const { SSH_PATH } = paths(); +// const { domain, port } = sanitizeRepoPathSSH(repositoryURL); +// const knownHostsPath = path.join(SSH_PATH, "known_hosts"); - const command = `ssh-keyscan -p ${port} ${domain} >> ${knownHostsPath}`; - try { - await execAsync(command); - } catch (error) { - console.error(`Error adding host to known_hosts: ${error}`); - throw error; - } -}; +// const command = `ssh-keyscan -p ${port} ${domain} >> ${knownHostsPath}`; +// try { +// await execAsync(command); +// } catch (error) { +// console.error(`Error adding host to known_hosts: ${error}`); +// throw error; +// } +// }; const addHostToKnownHostsCommand = (repositoryURL: string) => { const { SSH_PATH } = paths(true); @@ -267,160 +147,43 @@ const sanitizeRepoPathSSH = (input: string) => { }; }; -export const cloneGitRawRepository = async (entity: { +interface Props { appName: string; - customGitUrl?: string | null; - customGitBranch?: string | null; - customGitSSHKeyId?: string | null; - enableSubmodules?: boolean; -}) => { - const { - appName, - customGitUrl, - customGitBranch, - customGitSSHKeyId, - enableSubmodules, - } = entity; + type?: "application" | "compose"; + serverId: string | null; +} - if (!customGitUrl || !customGitBranch) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error: Repository not found", - }); - } - - const { SSH_PATH, COMPOSE_PATH } = paths(); - const temporalKeyPath = path.join("/tmp", "id_rsa"); - const basePath = COMPOSE_PATH; +export const getGitCommitInfo = async ({ + appName, + type = "application", + serverId, +}: Props) => { + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(!!serverId); + const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); - const knownHostsPath = path.join(SSH_PATH, "known_hosts"); - - if (customGitSSHKeyId) { - const sshKey = await findSSHKeyById(customGitSSHKeyId); - - await execAsync(` - echo "${sshKey.privateKey}" > ${temporalKeyPath} - chmod 600 ${temporalKeyPath} - `); - } - + let stdoutResult = ""; + const result = { + message: "", + hash: "", + }; try { - if (!isHttpOrHttps(customGitUrl)) { - if (!customGitSSHKeyId) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key", - }); - } - await addHostToKnownHosts(customGitUrl); - } - await recreateDirectory(outputPath); - - if (customGitSSHKeyId) { - await updateSSHKeyById({ - sshKeyId: customGitSSHKeyId, - lastUsedAt: new Date().toISOString(), - }); + const gitCommand = `git -C ${outputPath} log -1 --pretty=format:"%H---DELIMITER---%B"`; + if (serverId) { + const { stdout } = await execAsyncRemote(serverId, gitCommand); + stdoutResult = stdout.trim(); + } else { + const { stdout } = await execAsync(gitCommand); + stdoutResult = stdout.trim(); } - const { port } = sanitizeRepoPathSSH(customGitUrl); - const cloneArgs = [ - "clone", - "--branch", - customGitBranch, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - customGitUrl, - outputPath, - "--progress", - ]; - - await spawnAsync("git", cloneArgs, (_data) => {}, { - env: { - ...process.env, - ...(customGitSSHKeyId && { - GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`, - }), - }, - }); + const parts = stdoutResult.split("---DELIMITER---"); + if (parts && parts.length === 2) { + result.hash = parts[0]?.trim() || ""; + result.message = parts[1]?.trim() || ""; + } } catch (error) { - throw error; - } -}; - -export const cloneRawGitRepositoryRemote = async (compose: Compose) => { - const { - appName, - customGitBranch, - customGitUrl, - customGitSSHKeyId, - serverId, - enableSubmodules, - } = compose; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - if (!customGitUrl) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Git Provider not found", - }); - } - - const { SSH_PATH, COMPOSE_PATH } = paths(true); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - const knownHostsPath = path.join(SSH_PATH, "known_hosts"); - - if (customGitSSHKeyId) { - await updateSSHKeyById({ - sshKeyId: customGitSSHKeyId, - lastUsedAt: new Date().toISOString(), - }); - } - try { - const command = []; - if (!isHttpOrHttps(customGitUrl)) { - if (!customGitSSHKeyId) { - command.push( - `echo "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key ❌" ; - exit 1; - `, - ); - } - command.push(addHostToKnownHostsCommand(customGitUrl)); - } - command.push(`rm -rf ${outputPath};`); - command.push(`mkdir -p ${outputPath};`); - if (customGitSSHKeyId) { - const sshKey = await findSSHKeyById(customGitSSHKeyId); - const { port } = sanitizeRepoPathSSH(customGitUrl); - const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`; - command.push( - ` - echo "${sshKey.privateKey}" > /tmp/id_rsa - chmod 600 /tmp/id_rsa - export GIT_SSH_COMMAND="${gitSshCommand}" - `, - ); - } - - command.push( - `if ! git clone --branch ${customGitBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${customGitUrl} ${outputPath} ; then - echo "[ERROR] Fail to clone the repository "; - exit 1; - fi - `, - ); - - await execAsyncRemote(serverId, command.join("\n")); - } catch (error) { - throw error; + console.error(`Error getting git commit info: ${error}`); + return null; } + return result; }; diff --git a/packages/server/src/utils/providers/gitea.ts b/packages/server/src/utils/providers/gitea.ts index db6dcbb78..ec8946ab3 100644 --- a/packages/server/src/utils/providers/gitea.ts +++ b/packages/server/src/utils/providers/gitea.ts @@ -1,7 +1,5 @@ -import { createWriteStream } from "node:fs"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; -import type { Compose } from "@dokploy/server/services/compose"; import { findGiteaById, type Gitea, @@ -9,9 +7,6 @@ import { } from "@dokploy/server/services/gitea"; import type { InferResultType } from "@dokploy/server/types/with"; import { TRPCError } from "@trpc/server"; -import { recreateDirectory } from "../filesystem/directory"; -import { execAsyncRemote } from "../process/execAsync"; -import { spawnAsync } from "../process/spawnAsync"; export const getErrorCloneRequirements = (entity: { giteaRepository?: string | null; @@ -119,79 +114,27 @@ export type ApplicationWithGitea = InferResultType< export type ComposeWithGitea = InferResultType<"compose", { gitea: true }>; -export const getGiteaCloneCommand = async ( - entity: ApplicationWithGitea | ComposeWithGitea, - logPath: string, - isCompose = false, -) => { - const { - appName, - giteaBranch, - giteaId, - giteaOwner, - giteaRepository, - serverId, - enableSubmodules, - } = entity; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - - if (!giteaId) { - const command = ` - echo "Error: ❌ Gitlab Provider not found" >> ${logPath}; - exit 1; - `; - - await execAsyncRemote(serverId, command); - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitea Provider not found", - }); - } - - // Use paths(true) for remote operations - const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); - await refreshGiteaToken(giteaId); - const gitea = await findGiteaById(giteaId); - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; - const outputPath = join(basePath, appName, "code"); - - const repoClone = `${giteaOwner}/${giteaRepository}.git`; - const cloneUrl = buildGiteaCloneUrl( - gitea?.giteaUrl!, - gitea?.accessToken!, - giteaOwner!, - giteaRepository!, - ); - - const cloneCommand = ` - rm -rf ${outputPath}; - mkdir -p ${outputPath}; - - if ! git clone --branch ${giteaBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then - echo "❌ [ERROR] Failed to clone the repository ${repoClone}" >> ${logPath}; - exit 1; - fi - - echo "Cloned ${repoClone} to ${outputPath}: ✅" >> ${logPath}; - `; - - return cloneCommand; +type GiteaClone = (ApplicationWithGitea | ComposeWithGitea) & { + serverId: string | null; + type?: "application" | "compose"; }; -export const cloneGiteaRepository = async ( - entity: ApplicationWithGitea | ComposeWithGitea, - logPath: string, - isCompose = false, -) => { - const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(); +interface CloneGiteaRepository { + appName: string; + giteaBranch: string | null; + giteaId: string | null; + giteaOwner: string | null; + giteaRepository: string | null; + enableSubmodules: boolean; + serverId: string | null; + type?: "application" | "compose"; +} - const writeStream = createWriteStream(logPath, { flags: "a" }); +export const cloneGiteaRepository = async ({ + type = "application", + ...entity +}: CloneGiteaRepository) => { + let command = "set -e;"; const { appName, giteaBranch, @@ -199,27 +142,27 @@ export const cloneGiteaRepository = async ( giteaOwner, giteaRepository, enableSubmodules, + serverId, } = entity; + const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(!!serverId); if (!giteaId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitea Provider not found", - }); + command += `echo "Error: ❌ Gitea Provider not found"; exit 1;`; + return command; } await refreshGiteaToken(giteaId); const giteaProvider = await findGiteaById(giteaId); + if (!giteaProvider) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitea provider not found in the database", - }); + command += `echo "❌ [ERROR] Gitea provider not found in the database"; exit 1;`; + return command; } - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; + const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); + command += `rm -rf ${outputPath};`; + command += `mkdir -p ${outputPath};`; const repoClone = `${giteaOwner}/${giteaRepository}.git`; const cloneUrl = buildGiteaCloneUrl( @@ -229,134 +172,9 @@ export const cloneGiteaRepository = async ( giteaRepository!, ); - writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}...\n`); - - try { - await spawnAsync( - "git", - [ - "clone", - "--branch", - giteaBranch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ], - (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }, - ); - writeStream.write(`\nCloned ${repoClone}: ✅\n`); - } catch (error) { - writeStream.write(`ERROR Cloning: ${error}: ❌`); - throw error; - } finally { - writeStream.end(); - } -}; - -export const cloneRawGiteaRepository = async (entity: Compose) => { - const { - appName, - giteaRepository, - giteaOwner, - giteaBranch, - giteaId, - enableSubmodules, - } = entity; - const { COMPOSE_PATH } = paths(); - - if (!giteaId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitea Provider not found", - }); - } - await refreshGiteaToken(giteaId); - const giteaProvider = await findGiteaById(giteaId); - if (!giteaProvider) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitea provider not found in the database", - }); - } - - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); - - const cloneUrl = buildGiteaCloneUrl( - giteaProvider.giteaUrl, - giteaProvider.accessToken!, - giteaOwner!, - giteaRepository!, - ); - - try { - await spawnAsync("git", [ - "clone", - "--branch", - giteaBranch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ]); - } catch (error) { - throw error; - } -}; - -export const cloneRawGiteaRepositoryRemote = async (compose: Compose) => { - const { - appName, - giteaRepository, - giteaOwner, - giteaBranch, - giteaId, - serverId, - enableSubmodules, - } = compose; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - if (!giteaId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitea Provider not found", - }); - } - const { COMPOSE_PATH } = paths(true); - const giteaProvider = await findGiteaById(giteaId); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - const cloneUrl = buildGiteaCloneUrl( - giteaProvider.giteaUrl, - giteaProvider.accessToken!, - giteaOwner!, - giteaRepository!, - ); - - try { - const command = ` - rm -rf ${outputPath}; - git clone --branch ${giteaBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} - `; - await execAsyncRemote(serverId, command); - } catch (error) { - throw error; - } + command += `echo "Cloning Repo ${repoClone} to ${outputPath}: ✅";`; + command += `git clone --branch ${giteaBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`; + return command; }; export const haveGiteaRequirements = (giteaProvider: Gitea) => { diff --git a/packages/server/src/utils/providers/github.ts b/packages/server/src/utils/providers/github.ts index 30125db8b..c0f1b651a 100644 --- a/packages/server/src/utils/providers/github.ts +++ b/packages/server/src/utils/providers/github.ts @@ -1,16 +1,11 @@ -import { createWriteStream } from "node:fs"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { apiFindGithubBranches } from "@dokploy/server/db/schema"; -import type { Compose } from "@dokploy/server/services/compose"; import { findGithubById, type Github } from "@dokploy/server/services/github"; import type { InferResultType } from "@dokploy/server/types/with"; import { createAppAuth } from "@octokit/auth-app"; import { TRPCError } from "@trpc/server"; import { Octokit } from "octokit"; -import { recreateDirectory } from "../filesystem/directory"; -import { execAsyncRemote } from "../process/execAsync"; -import { spawnAsync } from "../process/spawnAsync"; export const authGithub = (githubProvider: Github): Octokit => { if (!haveGithubRequirements(githubProvider)) { @@ -123,42 +118,39 @@ interface CloneGithubRepository { branch: string | null; githubId: string | null; repository: string | null; - logPath: string; type?: "application" | "compose"; enableSubmodules: boolean; + serverId: string | null; } export const cloneGithubRepository = async ({ - logPath, type = "application", ...entity }: CloneGithubRepository) => { + let command = "set -e;"; const isCompose = type === "compose"; - const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(); - const writeStream = createWriteStream(logPath, { flags: "a" }); - const { appName, repository, owner, branch, githubId, enableSubmodules } = - entity; + const { + appName, + repository, + owner, + branch, + githubId, + enableSubmodules, + serverId, + } = entity; + const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(!!serverId); if (!githubId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "GitHub Provider not found", - }); + command += `echo "Error: ❌ Github Provider not found"; exit 1;`; + + return command; } const requirements = getErrorCloneRequirements(entity); // Check if requirements are met if (requirements.length > 0) { - writeStream.write( - `\nGitHub Repository configuration failed for application: ${appName}\n`, - ); - writeStream.write("Reasons:\n"); - writeStream.write(requirements.join("\n")); - writeStream.end(); - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error: GitHub repository information is incomplete.", - }); + command += `echo "GitHub Repository configuration failed for application: ${appName}"; echo "Reasons:"; echo "${requirements.join("\n")}"; exit 1;`; + return command; } const githubProvider = await findGithubById(githubId); @@ -167,193 +159,15 @@ export const cloneGithubRepository = async ({ const octokit = authGithub(githubProvider); const token = await getGithubToken(octokit); const repoclone = `github.com/${owner}/${repository}.git`; - await recreateDirectory(outputPath); + // await recreateDirectory(outputPath); + command += `rm -rf ${outputPath};`; + command += `mkdir -p ${outputPath};`; const cloneUrl = `https://oauth2:${token}@${repoclone}`; - try { - writeStream.write(`\nCloning Repo ${repoclone} to ${outputPath}: ✅\n`); - const cloneArgs = [ - "clone", - "--branch", - branch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ]; + command += `echo "Cloning Repo ${repoclone} to ${outputPath}: ✅";`; + command += `git clone --branch ${branch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`; - await spawnAsync("git", cloneArgs, (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - writeStream.write(`\nCloned ${repoclone}: ✅\n`); - } catch (error) { - writeStream.write(`ERROR Cloning: ${error}: ❌`); - throw error; - } finally { - writeStream.end(); - } -}; - -export const getGithubCloneCommand = async ({ - logPath, - type = "application", - ...entity -}: CloneGithubRepository & { serverId: string }) => { - const { - appName, - repository, - owner, - branch, - githubId, - serverId, - enableSubmodules, - } = entity; - const isCompose = type === "compose"; - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - - if (!githubId) { - const command = ` - echo "Error: ❌ Github Provider not found" >> ${logPath}; - exit 1; - `; - - await execAsyncRemote(serverId, command); - throw new TRPCError({ - code: "NOT_FOUND", - message: "GitHub Provider not found", - }); - } - - const requirements = getErrorCloneRequirements(entity); - - // Build log messages - let logMessages = ""; - if (requirements.length > 0) { - logMessages += `\nGitHub Repository configuration failed for application: ${appName}\n`; - logMessages += "Reasons:\n"; - logMessages += requirements.join("\n"); - const escapedLogMessages = logMessages - .replace(/\\/g, "\\\\") - .replace(/"/g, '\\"') - .replace(/\n/g, "\\n"); - - const bashCommand = ` - echo "${escapedLogMessages}" >> ${logPath}; - exit 1; # Exit with error code - `; - - await execAsyncRemote(serverId, bashCommand); - return; - } - const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); - const githubProvider = await findGithubById(githubId); - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; - const outputPath = join(basePath, appName, "code"); - const octokit = authGithub(githubProvider); - const token = await getGithubToken(octokit); - const repoclone = `github.com/${owner}/${repository}.git`; - const cloneUrl = `https://oauth2:${token}@${repoclone}`; - - const cloneCommand = ` -rm -rf ${outputPath}; -mkdir -p ${outputPath}; -if ! git clone --branch ${branch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then - echo "❌ [ERROR] Fail to clone repository ${repoclone}" >> ${logPath}; - exit 1; -fi -echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath}; - `; - - return cloneCommand; -}; - -export const cloneRawGithubRepository = async (entity: Compose) => { - const { appName, repository, owner, branch, githubId, enableSubmodules } = - entity; - - if (!githubId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "GitHub Provider not found", - }); - } - const { COMPOSE_PATH } = paths(); - const githubProvider = await findGithubById(githubId); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - const octokit = authGithub(githubProvider); - const token = await getGithubToken(octokit); - const repoclone = `github.com/${owner}/${repository}.git`; - await recreateDirectory(outputPath); - const cloneUrl = `https://oauth2:${token}@${repoclone}`; - try { - const cloneArgs = [ - "clone", - "--branch", - branch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ]; - await spawnAsync("git", cloneArgs); - } catch (error) { - throw error; - } -}; - -export const cloneRawGithubRepositoryRemote = async (compose: Compose) => { - const { - appName, - repository, - owner, - branch, - githubId, - serverId, - enableSubmodules, - } = compose; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - if (!githubId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "GitHub Provider not found", - }); - } - - const { COMPOSE_PATH } = paths(true); - const githubProvider = await findGithubById(githubId); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - const octokit = authGithub(githubProvider); - const token = await getGithubToken(octokit); - const repoclone = `github.com/${owner}/${repository}.git`; - const cloneUrl = `https://oauth2:${token}@${repoclone}`; - try { - const command = ` - rm -rf ${outputPath}; - git clone --branch ${branch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} - `; - await execAsyncRemote(serverId, command); - } catch (error) { - throw error; - } + return command; }; export const getGithubRepositories = async (githubId?: string) => { diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index 840347fdb..a3106b8c0 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -1,8 +1,6 @@ -import { createWriteStream } from "node:fs"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { apiGitlabTestConnection } from "@dokploy/server/db/schema"; -import type { Compose } from "@dokploy/server/services/compose"; import { findGitlabById, type Gitlab, @@ -10,9 +8,6 @@ import { } from "@dokploy/server/services/gitlab"; import type { InferResultType } from "@dokploy/server/types/with"; import { TRPCError } from "@trpc/server"; -import { recreateDirectory } from "../filesystem/directory"; -import { execAsyncRemote } from "../process/execAsync"; -import { spawnAsync } from "../process/spawnAsync"; export const refreshGitlabToken = async (gitlabProviderId: string) => { const gitlabProvider = await findGitlabById(gitlabProviderId); @@ -102,25 +97,34 @@ const getGitlabCloneUrl = (gitlab: GitlabInfo, repoClone: string) => { return cloneUrl; }; -export const cloneGitlabRepository = async ( - entity: ApplicationWithGitlab | ComposeWithGitlab, - logPath: string, - isCompose = false, -) => { - const writeStream = createWriteStream(logPath, { flags: "a" }); +interface CloneGitlabRepository { + appName: string; + gitlabBranch: string | null; + gitlabId: string | null; + gitlabPathNamespace: string | null; + enableSubmodules: boolean; + serverId: string | null; + type?: "application" | "compose"; +} + +export const cloneGitlabRepository = async ({ + type = "application", + ...entity +}: CloneGitlabRepository) => { + let command = "set -e;"; const { appName, gitlabBranch, gitlabId, gitlabPathNamespace, enableSubmodules, + serverId, } = entity; + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(!!serverId); if (!gitlabId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitlab Provider not found", - }); + command += `echo "Error: ❌ Gitlab Provider not found"; exit 1;`; + return command; } await refreshGitlabToken(gitlabId); @@ -130,127 +134,19 @@ export const cloneGitlabRepository = async ( // Check if requirements are met if (requirements.length > 0) { - writeStream.write( - `\nGitLab Repository configuration failed for application: ${appName}\n`, - ); - writeStream.write("Reasons:\n"); - writeStream.write(requirements.join("\n")); - writeStream.end(); - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error: GitLab repository information is incomplete.", - }); + command += `echo "❌ [ERROR] GitLab Repository configuration failed for application: ${appName}"; echo "Reasons:"; echo "${requirements.join("\n")}"; exit 1;`; + return command; } - const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(); - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; + const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); + command += `rm -rf ${outputPath};`; + command += `mkdir -p ${outputPath};`; const repoClone = getGitlabRepoClone(gitlab, gitlabPathNamespace); const cloneUrl = getGitlabCloneUrl(gitlab, repoClone); - try { - writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}: ✅\n`); - const cloneArgs = [ - "clone", - "--branch", - gitlabBranch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ]; - - await spawnAsync("git", cloneArgs, (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - writeStream.write(`\nCloned ${repoClone}: ✅\n`); - } catch (error) { - writeStream.write(`ERROR Cloning: ${error}: ❌`); - throw error; - } finally { - writeStream.end(); - } -}; - -export const getGitlabCloneCommand = async ( - entity: ApplicationWithGitlab | ComposeWithGitlab, - logPath: string, - isCompose = false, -) => { - const { - appName, - gitlabPathNamespace, - gitlabBranch, - gitlabId, - serverId, - enableSubmodules, - } = entity; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - - if (!gitlabId) { - const command = ` - echo "Error: ❌ Gitlab Provider not found" >> ${logPath}; - exit 1; - `; - - await execAsyncRemote(serverId, command); - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitlab Provider not found", - }); - } - - const requirements = getErrorCloneRequirements(entity); - - // Build log messages - let logMessages = ""; - if (requirements.length > 0) { - logMessages += `\nGitLab Repository configuration failed for application: ${appName}\n`; - logMessages += "Reasons:\n"; - logMessages += requirements.join("\n"); - const escapedLogMessages = logMessages - .replace(/\\/g, "\\\\") - .replace(/"/g, '\\"') - .replace(/\n/g, "\\n"); - - const bashCommand = ` - echo "${escapedLogMessages}" >> ${logPath}; - exit 1; # Exit with error code - `; - - await execAsyncRemote(serverId, bashCommand); - return; - } - - const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); - await refreshGitlabToken(gitlabId); - const gitlab = await findGitlabById(gitlabId); - const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; - const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); - const repoClone = getGitlabRepoClone(gitlab, gitlabPathNamespace); - const cloneUrl = getGitlabCloneUrl(gitlab, repoClone); - const cloneCommand = ` -rm -rf ${outputPath}; -mkdir -p ${outputPath}; -if ! git clone --branch ${gitlabBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then - echo "❌ [ERROR] Fail to clone the repository ${repoClone}" >> ${logPath}; - exit 1; -fi -echo "Cloned ${repoClone} to ${outputPath}: ✅" >> ${logPath}; - `; - - return cloneCommand; + command += `echo "Cloning Repo ${repoClone} to ${outputPath}: ✅";`; + command += `git clone --branch ${gitlabBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`; + return command; }; export const getGitlabRepositories = async (gitlabId?: string) => { @@ -355,88 +251,6 @@ export const getGitlabBranches = async (input: { }[]; }; -export const cloneRawGitlabRepository = async (entity: Compose) => { - const { - appName, - gitlabBranch, - gitlabId, - gitlabPathNamespace, - enableSubmodules, - } = entity; - - if (!gitlabId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitlab Provider not found", - }); - } - - const { COMPOSE_PATH } = paths(); - await refreshGitlabToken(gitlabId); - const gitlabProvider = await findGitlabById(gitlabId); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - await recreateDirectory(outputPath); - const repoClone = getGitlabRepoClone(gitlabProvider, gitlabPathNamespace); - const cloneUrl = getGitlabCloneUrl(gitlabProvider, repoClone); - try { - const cloneArgs = [ - "clone", - "--branch", - gitlabBranch!, - "--depth", - "1", - ...(enableSubmodules ? ["--recurse-submodules"] : []), - cloneUrl, - outputPath, - "--progress", - ]; - await spawnAsync("git", cloneArgs); - } catch (error) { - throw error; - } -}; - -export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => { - const { - appName, - gitlabPathNamespace, - gitlabBranch, - gitlabId, - serverId, - enableSubmodules, - } = compose; - - if (!serverId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server not found", - }); - } - if (!gitlabId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Gitlab Provider not found", - }); - } - const { COMPOSE_PATH } = paths(true); - await refreshGitlabToken(gitlabId); - const gitlabProvider = await findGitlabById(gitlabId); - const basePath = COMPOSE_PATH; - const outputPath = join(basePath, appName, "code"); - const repoClone = getGitlabRepoClone(gitlabProvider, gitlabPathNamespace); - const cloneUrl = getGitlabCloneUrl(gitlabProvider, repoClone); - try { - const command = ` - rm -rf ${outputPath}; - git clone --branch ${gitlabBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} - `; - await execAsyncRemote(serverId, command); - } catch (error) { - throw error; - } -}; - export const testGitlabConnection = async ( input: typeof apiGitlabTestConnection._type, ) => { diff --git a/packages/server/src/utils/providers/raw.ts b/packages/server/src/utils/providers/raw.ts index 34ba0012a..508df86ed 100644 --- a/packages/server/src/utils/providers/raw.ts +++ b/packages/server/src/utils/providers/raw.ts @@ -1,40 +1,10 @@ -import { createWriteStream } from "node:fs"; -import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { Compose } from "@dokploy/server/services/compose"; import { encodeBase64 } from "../docker/utils"; -import { recreateDirectory } from "../filesystem/directory"; -import { execAsyncRemote } from "../process/execAsync"; -export const createComposeFile = async (compose: Compose, logPath: string) => { - const { COMPOSE_PATH } = paths(); - const { appName, composeFile } = compose; - const writeStream = createWriteStream(logPath, { flags: "a" }); - const outputPath = join(COMPOSE_PATH, appName, "code"); - - try { - await recreateDirectory(outputPath); - writeStream.write( - `\nCreating File 'docker-compose.yml' to ${outputPath}: ✅\n`, - ); - - await writeFile(join(outputPath, "docker-compose.yml"), composeFile); - - writeStream.write(`\nFile 'docker-compose.yml' created: ✅\n`); - } catch (error) { - writeStream.write(`\nERROR Creating Compose File: ${error}: ❌\n`); - throw error; - } finally { - writeStream.end(); - } -}; - -export const getCreateComposeFileCommand = ( - compose: Compose, - logPath: string, -) => { - const { COMPOSE_PATH } = paths(true); +export const getCreateComposeFileCommand = (compose: Compose) => { + const { COMPOSE_PATH } = paths(!!compose.serverId); const { appName, composeFile } = compose; const outputPath = join(COMPOSE_PATH, appName, "code"); const filePath = join(outputPath, "docker-compose.yml"); @@ -43,39 +13,7 @@ export const getCreateComposeFileCommand = ( rm -rf ${outputPath}; mkdir -p ${outputPath}; echo "${encodedContent}" | base64 -d > "${filePath}"; - echo "File 'docker-compose.yml' created: ✅" >> ${logPath}; + echo "File 'docker-compose.yml' created: ✅"; `; return bashCommand; }; - -export const createComposeFileRaw = async (compose: Compose) => { - const { COMPOSE_PATH } = paths(); - 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; - } -}; - -export const createComposeFileRawRemote = async (compose: Compose) => { - const { COMPOSE_PATH } = paths(true); - const { appName, composeFile, serverId } = compose; - const outputPath = join(COMPOSE_PATH, appName, "code"); - const filePath = join(outputPath, "docker-compose.yml"); - - try { - const encodedContent = encodeBase64(composeFile); - const command = ` - rm -rf ${outputPath}; - mkdir -p ${outputPath}; - echo "${encodedContent}" | base64 -d > "${filePath}"; - `; - await execAsyncRemote(serverId, command); - } catch (error) { - throw error; - } -}; diff --git a/packages/server/src/utils/restore/utils.ts b/packages/server/src/utils/restore/utils.ts index c46077238..23052e642 100644 --- a/packages/server/src/utils/restore/utils.ts +++ b/packages/server/src/utils/restore/utils.ts @@ -7,7 +7,7 @@ export const getPostgresRestoreCommand = ( database: string, databaseUser: string, ) => { - return `docker exec -i $CONTAINER_ID sh -c "pg_restore -U ${databaseUser} -d ${database} -O --clean --if-exists"`; + return `docker exec -i $CONTAINER_ID sh -c "pg_restore -U '${databaseUser}' -d ${database} -O --clean --if-exists"`; }; export const getMariadbRestoreCommand = ( @@ -15,14 +15,14 @@ export const getMariadbRestoreCommand = ( databaseUser: string, databasePassword: string, ) => { - return `docker exec -i $CONTAINER_ID sh -c "mariadb -u ${databaseUser} -p${databasePassword} ${database}"`; + return `docker exec -i $CONTAINER_ID sh -c "mariadb -u '${databaseUser}' -p'${databasePassword}' ${database}"`; }; export const getMysqlRestoreCommand = ( database: string, databasePassword: string, ) => { - return `docker exec -i $CONTAINER_ID sh -c "mysql -u root -p${databasePassword} ${database}"`; + return `docker exec -i $CONTAINER_ID sh -c "mysql -u root -p'${databasePassword}' ${database}"`; }; export const getMongoRestoreCommand = ( @@ -30,7 +30,7 @@ export const getMongoRestoreCommand = ( databaseUser: string, databasePassword: string, ) => { - return `docker exec -i $CONTAINER_ID sh -c "mongorestore --username ${databaseUser} --password ${databasePassword} --authenticationDatabase admin --db ${database} --archive"`; + return `docker exec -i $CONTAINER_ID sh -c "mongorestore --username '${databaseUser}' --password '${databasePassword}' --authenticationDatabase admin --db ${database} --archive"`; }; export const getComposeSearchCommand = ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1aae074f7..ba76d1b73 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -347,8 +347,8 @@ importers: specifier: ^0.2.1 version: 0.2.1(next@15.3.2(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) node-os-utils: - specifier: 1.3.7 - version: 1.3.7 + specifier: 2.0.1 + version: 2.0.1 node-pty: specifier: 1.0.0 version: 1.0.0 @@ -473,9 +473,6 @@ importers: '@types/node': specifier: ^18.19.104 version: 18.19.104 - '@types/node-os-utils': - specifier: 1.3.4 - version: 1.3.4 '@types/node-schedule': specifier: 2.1.6 version: 2.1.6 @@ -688,8 +685,8 @@ importers: specifier: 3.3.11 version: 3.3.11 node-os-utils: - specifier: 1.3.7 - version: 1.3.7 + specifier: 2.0.1 + version: 2.0.1 node-pty: specifier: 1.0.0 version: 1.0.0 @@ -766,9 +763,6 @@ importers: '@types/node': specifier: ^18.19.104 version: 18.19.104 - '@types/node-os-utils': - specifier: 1.3.4 - version: 1.3.4 '@types/node-schedule': specifier: 2.1.6 version: 2.1.6 @@ -4000,9 +3994,6 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} - '@types/node-os-utils@1.3.4': - resolution: {integrity: sha512-BCUYrbdoO4FUbx6MB9atLNFnkxdliFaxdiTJMIPPiecXIApc5zf4NIqV5G1jWv/ReZvtYyHLs40RkBjHX+vykA==} - '@types/node-schedule@2.1.6': resolution: {integrity: sha512-6AlZSUiNTdaVmH5jXYxX9YgmF1zfOlbjUqw0EllTBmZCnN1R5RR/m/u3No1OiWR05bnQ4jM4/+w4FcGvkAtnKQ==} @@ -6312,8 +6303,9 @@ packages: '@types/node': optional: true - node-os-utils@1.3.7: - resolution: {integrity: sha512-fvnX9tZbR7WfCG5BAy3yO/nCLyjVWD6MghEq0z5FDfN+ZXpLWNITBdbifxQkQ25ebr16G0N7eRWJisOcMEHG3Q==} + node-os-utils@2.0.1: + resolution: {integrity: sha512-rH2N3qHZETLhdgTGhMMCE8zU3gsWO4we1MFtrSiAI7tYWrnJRc6dk2PseV4co3Lb0v/MbRONLQI2biHQYbpTpg==} + engines: {node: '>=18.0.0'} node-pty@1.0.0: resolution: {integrity: sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==} @@ -11338,8 +11330,6 @@ snapshots: dependencies: '@types/node': 20.17.51 - '@types/node-os-utils@1.3.4': {} - '@types/node-schedule@2.1.6': dependencies: '@types/node': 20.17.51 @@ -13852,7 +13842,7 @@ snapshots: optionalDependencies: '@types/node': 18.19.104 - node-os-utils@1.3.7: {} + node-os-utils@2.0.1: {} node-pty@1.0.0: dependencies: