diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx index c018a97cc..24446902d 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx @@ -14,7 +14,8 @@ interface Props { | "schedule" | "server" | "backup" - | "previewDeployment"; + | "previewDeployment" + | "volumeBackup"; serverId?: string; refreshToken?: string; children?: React.ReactNode; diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx index b8e773df7..04631b9b3 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx @@ -27,7 +27,8 @@ interface Props { | "schedule" | "server" | "backup" - | "previewDeployment"; + | "previewDeployment" + | "volumeBackup"; refreshToken?: string; serverId?: string; } diff --git a/apps/dokploy/components/dashboard/application/rollbacks/Backup b/apps/dokploy/components/dashboard/application/rollbacks/Backup new file mode 100644 index 000000000..2a58e92df --- /dev/null +++ b/apps/dokploy/components/dashboard/application/rollbacks/Backup @@ -0,0 +1,108 @@ +Backup +# license-namedbackups-abxelc +1. docker ps --filter "label=com.docker.swarm.service.name=license-namedbackups-abxelc" --format "{{.Names}}" +2. docker run --rm \ +--volumes-from "license-namedbackups-abxelc.1.m3cxy78ocj3w0zu42kmgamc5y" \ +-v $(pwd):/backup \ +ubuntu \ +tar cvf /backup/backup.tar /var/lib/postgresql/data + + +# Official Command Backup + +1. Backup + +docker run --rm \ + -v license-namedbackups-abxelc-data:/volume_data \ + -v $(pwd):/backup \ + ubuntu \ + bash -c "cd /volume_data && tar cvf /backup/generic_backup.tar ." + + +2. Restore + +docker service scale license-namedbackups-abxelc=0 + +docker volume rm license-namedbackups-abxelc-data + +2. docker run --rm \ +-v license-namedbackups-abxelc-data:/volume_data \ +-v $(pwd):/backup \ +ubuntu \ +bash -c "cd /volume_data && tar xvf /backup/generic_backup.tar ." + +docker service scale license-namedbackups-abxelc=1 + + +root@srv594061:~# docker volume inspect n8n_data-data +[ + { + "CreatedAt": "2025-06-28T18:07:44Z", + "Driver": "local", + "Labels": null, + "Mountpoint": "/var/lib/docker/volumes/n8n_data-data/_data", + "Name": "n8n_data-data", + "Options": null, + "Scope": "local" + } +] + +Archivos funcuionando creados por N8N + +# root@srv594061:~# cd /var/lib/docker/volumes/n8n_data-data/_data +# root@srv594061:/var/lib/docker/volumes/n8n_data-data/_data# ls +# binaryData config crash.journal database.sqlite git n8nEventLog.log ssh + +Luego que intente hacer el backup con el comando de backup + + +root@srv594061:~# docker run --rm -v n8n_data-data:/volume_data -v $(pwd):/backup ubuntu bash -c "cd /volume_data && tar cvf /backup/generic_backup6.tar ." +./ +./config +./crash.journal +./binaryData/ +./git/ +./database.sqlite +./ssh/ +./n8nEventLog.log +root@srv594061:~# + +# Paramos la aplicacion +docker service scale n8n=0 + +# Haciendo el restore +root@srv594061:~# docker volume rm n8n_data-data +n8n_data-data +root@srv594061:~# docker run --rm -v n8n_data-data:/volume_data -v $(pwd):/backup ubuntu bash -c "cd /volume_data && tar xvf /backup/generic_backup6.tar && chown -R 999:999 ." +./ +./config +./crash.journal +./binaryData/ +./git/ +./database.sqlite +./ssh/ +./n8nEventLog.log + +# Tenemos los archivos en el volumen +root@srv594061:~# ls /var/lib/docker/volumes/n8n_data-data/_data +binaryData config crash.journal database.sqlite git n8nEventLog.log ssh +root@srv594061:~# + +docker service scale n8n=1 + +# Luego en N8N Cuando se que el volumen tiene la data +Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. This is ignored for now, but in the future n8n will attempt to change the permissions automatically. To automatically enforce correct permissions now set N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true (recommended), or turn this check off set N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false. +User settings loaded from: /home/node/.n8n/config +Last session crashed +Error: EACCES: permission denied, open '/home/node/.n8n/crash.journal' +at open (node:internal/fs/promises:639:25) +at touchFile (/usr/local/lib/node_modules/n8n/dist/crash-journal.js:18:20) +at Object.init (/usr/local/lib/node_modules/n8n/dist/crash-journal.js:32:5) +at Start.initCrashJournal (/usr/local/lib/node_modules/n8n/dist/commands/base-command.js:113:9) +at Start.init (/usr/local/lib/node_modules/n8n/dist/commands/start.js:141:9) +at Start._run (/usr/local/lib/node_modules/n8n/node_modules/@oclif/core/lib/command.js:301:13) +at Config.runCommand (/usr/local/lib/node_modules/n8n/node_modules/@oclif/core/lib/config/config.js:424:25) +at run (/usr/local/lib/node_modules/n8n/node_modules/@oclif/core/lib/main.js:94:16) +at /usr/local/lib/node_modules/n8n/bin/n8n:71:2 +TypeError: Cannot read properties of undefined (reading 'error') + diff --git a/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx new file mode 100644 index 000000000..aee797e45 --- /dev/null +++ b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx @@ -0,0 +1,672 @@ +import { AlertBlock } from "@/components/shared/alert-block"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + DatabaseZap, + Info, + PenBoxIcon, + PlusCircle, + RefreshCw, +} from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import type { CacheType } from "../domains/handle-domain"; +import { commonCronExpressions } from "../schedules/handle-schedules"; + +const formSchema = z + .object({ + name: z.string().min(1, "Name is required"), + cronExpression: z.string().min(1, "Cron expression is required"), + volumeName: z.string().min(1, "Volume name is required"), + prefix: z.string(), + keepLatestCount: z.coerce.number().optional(), + turnOff: z.boolean().default(false), + enabled: z.boolean().default(true), + serviceType: z.enum([ + "application", + "compose", + "postgres", + "mariadb", + "mongo", + "mysql", + "redis", + ]), + serviceName: z.string(), + destinationId: z.string().min(1, "Destination required"), + }) + .superRefine((data, ctx) => { + if (data.serviceType === "compose" && !data.serviceName) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Service name is required", + path: ["serviceName"], + }); + } + + if (data.serviceType === "compose" && !data.serviceName) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Service name is required", + path: ["serviceName"], + }); + } + }); + +interface Props { + id?: string; + volumeBackupId?: string; + volumeBackupType?: + | "application" + | "compose" + | "postgres" + | "mariadb" + | "mongo" + | "mysql" + | "redis"; +} + +export const HandleVolumeBackups = ({ + id, + volumeBackupId, + volumeBackupType, +}: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [cacheType, setCacheType] = useState("cache"); + + const utils = api.useUtils(); + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + name: "", + cronExpression: "", + volumeName: "", + prefix: "", + keepLatestCount: undefined, + turnOff: false, + enabled: true, + serviceName: "", + serviceType: volumeBackupType, + }, + }); + + const serviceTypeForm = volumeBackupType; + const { data: destinations } = api.destination.all.useQuery(); + const { data: volumeBackup } = api.volumeBackups.one.useQuery( + { volumeBackupId: volumeBackupId || "" }, + { enabled: !!volumeBackupId }, + ); + + const { data: mounts } = api.mounts.allNamedByApplicationId.useQuery( + { applicationId: id || "" }, + { enabled: !!id && volumeBackupType === "application" }, + ); + + const { + data: services, + isFetching: isLoadingServices, + error: errorServices, + refetch: refetchServices, + } = api.compose.loadServices.useQuery( + { + composeId: id || "", + type: cacheType, + }, + { + retry: false, + refetchOnWindowFocus: false, + enabled: !!id && volumeBackupType === "compose", + }, + ); + + const serviceName = form.watch("serviceName"); + + const { data: mountsByService } = api.compose.loadMountsByService.useQuery( + { + composeId: id || "", + serviceName, + }, + { + enabled: !!id && volumeBackupType === "compose" && !!serviceName, + }, + ); + + useEffect(() => { + if (volumeBackupId && volumeBackup) { + form.reset({ + name: volumeBackup.name, + cronExpression: volumeBackup.cronExpression, + volumeName: volumeBackup.volumeName || "", + prefix: volumeBackup.prefix, + keepLatestCount: volumeBackup.keepLatestCount || undefined, + turnOff: volumeBackup.turnOff, + enabled: volumeBackup.enabled || false, + serviceName: volumeBackup.serviceName || "", + destinationId: volumeBackup.destinationId, + serviceType: volumeBackup.serviceType, + }); + } + }, [form, volumeBackup, volumeBackupId]); + + const { mutateAsync, isLoading } = volumeBackupId + ? api.volumeBackups.update.useMutation() + : api.volumeBackups.create.useMutation(); + + const onSubmit = async (values: z.infer) => { + if (!id && !volumeBackupId) return; + + await mutateAsync({ + ...values, + destinationId: values.destinationId, + volumeBackupId: volumeBackupId || "", + serviceType: volumeBackupType, + ...(volumeBackupType === "application" && { + applicationId: id || "", + }), + ...(volumeBackupType === "compose" && { + composeId: id || "", + }), + ...(volumeBackupType === "postgres" && { + serverId: id || "", + }), + ...(volumeBackupType === "postgres" && { + postgresId: id || "", + }), + ...(volumeBackupType === "mariadb" && { + mariadbId: id || "", + }), + ...(volumeBackupType === "mongo" && { + mongoId: id || "", + }), + ...(volumeBackupType === "mysql" && { + mysqlId: id || "", + }), + ...(volumeBackupType === "redis" && { + redisId: id || "", + }), + }) + .then(() => { + toast.success( + `Volume backup ${volumeBackupId ? "updated" : "created"} successfully`, + ); + utils.volumeBackups.list.invalidate({ + id, + volumeBackupType, + }); + setIsOpen(false); + }) + .catch((error) => { + toast.error( + error instanceof Error ? error.message : "An unknown error occurred", + ); + }); + }; + + return ( + + + {volumeBackupId ? ( + + ) : ( + + )} + + + + + {volumeBackupId ? "Edit" : "Create"} Volume Backup + + + Create a volume backup to backup your volume to a destination + + +
+ + ( + + + Task Name + + + + + + A descriptive name for your scheduled task + + + + )} + /> + + ( + + + Schedule + + + + + + +

+ Cron expression format: minute hour day month + weekday +

+

Example: 0 0 * * * (daily at midnight)

+
+
+
+
+
+ +
+ + + +
+
+ + Choose a predefined schedule or enter a custom cron + expression + + +
+ )} + /> + + ( + + Destination + + + Choose the backup destination where files will be stored + + + + )} + /> + {serviceTypeForm === "compose" && ( + <> +
+ {errorServices && ( + + {errorServices?.message} + + )} + ( + + Service Name +
+ + + + + + + +

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

+
+
+
+ + + + + + +

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

+
+
+
+
+ + +
+ )} + /> +
+ {mountsByService && mountsByService.length > 0 && ( + ( + + Volumes + + + Choose the volume to backup, if you dont see the + volume here, you can type the volume name manually + + + + )} + /> + )} + + )} + {serviceTypeForm === "application" && ( + ( + + Volumes + + + Choose the volume to backup, if you dont see the volume + here, you can type the volume name manually + + + + )} + /> + )} + + ( + + Volume Name + + + + + The name of the Docker volume to backup + + + + )} + /> + + ( + + Backup Prefix + + + + + Prefix for backup files (optional) + + + + )} + /> + + ( + + Keep Latest Count + + + field.onChange(Number(e.target.value) || undefined) + } + /> + + + Number of backup files to keep (optional) + + + + )} + /> + + ( + + + + Turn Off Container During Backup + + + ⚠️ The container will be temporarily stopped during backup to + prevent file corruption. This ensures data integrity but may + cause temporary service interruption. + + + )} + /> + + ( + + + + Enabled + + + )} + /> + + + + +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx new file mode 100644 index 000000000..7c45d0ceb --- /dev/null +++ b/apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx @@ -0,0 +1,411 @@ +import { DrawerLogs } from "@/components/shared/drawer-logs"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, +} from "@/components/ui/command"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { cn } from "@/lib/utils"; +import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import copy from "copy-to-clipboard"; +import { debounce } from "lodash"; +import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { type LogLine, parseLogs } from "../../docker/logs/utils"; +import { formatBytes } from "../../database/backups/restore-backup"; +import { AlertBlock } from "@/components/shared/alert-block"; + +interface Props { + id: string; + type: "application" | "compose"; + serverId?: string; +} + +const RestoreBackupSchema = z.object({ + destinationId: z + .string({ + required_error: "Please select a destination", + }) + .min(1, { + message: "Destination is required", + }), + backupFile: z + .string({ + required_error: "Please select a backup file", + }) + .min(1, { + message: "Backup file is required", + }), + volumeName: z + .string({ + required_error: "Please enter a volume name", + }) + .min(1, { + message: "Volume name is required", + }), +}); + +export const RestoreVolumeBackups = ({ id, type, serverId }: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [search, setSearch] = useState(""); + const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(""); + + const { data: destinations = [] } = api.destination.all.useQuery(); + + const form = useForm>({ + defaultValues: { + destinationId: "", + backupFile: "", + volumeName: "", + }, + resolver: zodResolver(RestoreBackupSchema), + }); + + const destinationId = form.watch("destinationId"); + const volumeName = form.watch("volumeName"); + const backupFile = form.watch("backupFile"); + + const debouncedSetSearch = debounce((value: string) => { + setDebouncedSearchTerm(value); + }, 350); + + const handleSearchChange = (value: string) => { + setSearch(value); + debouncedSetSearch(value); + }; + + const { data: files = [], isLoading } = api.backup.listBackupFiles.useQuery( + { + destinationId: destinationId, + search: debouncedSearchTerm, + serverId: serverId ?? "", + }, + { + enabled: isOpen && !!destinationId, + }, + ); + + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [filteredLogs, setFilteredLogs] = useState([]); + const [isDeploying, setIsDeploying] = useState(false); + + api.volumeBackups.restoreVolumeBackupWithLogs.useSubscription( + { + id, + serviceType: type, + serverId, + destinationId, + volumeName, + backupFileName: backupFile, + }, + { + enabled: isDeploying, + onData(log) { + if (!isDrawerOpen) { + setIsDrawerOpen(true); + } + + if (log === "Restore completed successfully!") { + setIsDeploying(false); + } + const parsedLogs = parseLogs(log); + setFilteredLogs((prev) => [...prev, ...parsedLogs]); + }, + onError(error) { + console.error("Restore logs error:", error); + setIsDeploying(false); + }, + }, + ); + + const onSubmit = async () => { + setIsDeploying(true); + }; + + return ( + + + + + + + + + Restore Volume Backup + + + Select a destination and search for volume backup files + + + Make sure the volume name is not being used by another container. + + + +
+ + ( + + Destination + + + + + + + + + + No destinations found. + + + {destinations.map((destination) => ( + { + form.setValue( + "destinationId", + destination.destinationId, + ); + }} + > + {destination.name} + + + ))} + + + + + + + + )} + /> + + ( + + + Search Backup Files + {field.value && ( + + {field.value} + { + e.stopPropagation(); + e.preventDefault(); + copy(field.value); + toast.success("Backup file copied to clipboard"); + }} + /> + + )} + + + + + + + + + + + {isLoading ? ( +
+ Loading backup files... +
+ ) : files.length === 0 && search ? ( +
+ No backup files found for "{search}" +
+ ) : files.length === 0 ? ( +
+ No backup files available +
+ ) : ( + + + {files?.map((file) => ( + { + form.setValue("backupFile", file.Path); + if (file.IsDir) { + setSearch(`${file.Path}/`); + setDebouncedSearchTerm(`${file.Path}/`); + } else { + setSearch(file.Path); + setDebouncedSearchTerm(file.Path); + } + }} + > +
+
+ + {file.Path} + + + +
+
+ + Size: {formatBytes(file.Size)} + + {file.IsDir && ( + + Directory + + )} + {file.Hashes?.MD5 && ( + MD5: {file.Hashes.MD5} + )} +
+
+
+ ))} +
+
+ )} +
+
+
+ +
+ )} + /> + ( + + Volume Name + + + + + + )} + /> + + + + + + + + { + setIsDrawerOpen(false); + setFilteredLogs([]); + setIsDeploying(false); + // refetch(); + }} + filteredLogs={filteredLogs} + /> +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx new file mode 100644 index 000000000..bb071947e --- /dev/null +++ b/apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx @@ -0,0 +1,250 @@ +import { DialogAction } from "@/components/shared/dialog-action"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { api } from "@/utils/api"; +import { + ClipboardList, + DatabaseBackup, + Loader2, + Play, + Trash2, +} from "lucide-react"; +import { toast } from "sonner"; +import { HandleVolumeBackups } from "./handle-volume-backups"; +import { ShowDeploymentsModal } from "../deployments/show-deployments-modal"; +import { RestoreVolumeBackups } from "./restore-volume-backups"; + +interface Props { + id: string; + type?: "application" | "compose"; + serverId?: string; +} + +export const ShowVolumeBackups = ({ + id, + type = "application", + serverId, +}: Props) => { + const { + data: volumeBackups, + isLoading: isLoadingVolumeBackups, + refetch: refetchVolumeBackups, + } = api.volumeBackups.list.useQuery( + { + id: id || "", + volumeBackupType: type, + }, + { + enabled: !!id, + }, + ); + + const utils = api.useUtils(); + + const { mutateAsync: deleteVolumeBackup, isLoading: isDeleting } = + api.volumeBackups.delete.useMutation(); + + const { mutateAsync: runManually, isLoading } = + api.volumeBackups.runManually.useMutation(); + + return ( + + +
+
+ + Volume Backups + + + Schedule volume backups to run automatically at specified + intervals. + +
+ +
+ {volumeBackups && volumeBackups.length > 0 && ( + <> + + +
+ +
+ + )} +
+
+
+ + {isLoadingVolumeBackups ? ( +
+ + + Loading volume backups... + +
+ ) : volumeBackups && volumeBackups.length > 0 ? ( +
+ {volumeBackups.map((volumeBackup) => { + const serverId = + volumeBackup.application?.serverId || + volumeBackup.postgres?.serverId || + volumeBackup.mysql?.serverId || + volumeBackup.mariadb?.serverId || + volumeBackup.mongo?.serverId || + volumeBackup.redis?.serverId || + volumeBackup.compose?.serverId; + return ( +
+
+
+ +
+
+
+

+ {volumeBackup.name} +

+ + {volumeBackup.enabled ? "Enabled" : "Disabled"} + +
+
+ + Cron: {volumeBackup.cronExpression} + +
+
+
+ +
+ + + + + + + + + + + Run Manual Volume Backup + + + + + + + { + await deleteVolumeBackup({ + volumeBackupId: volumeBackup.volumeBackupId, + }) + .then(() => { + utils.volumeBackups.list.invalidate({ + id, + volumeBackupType: type, + }); + toast.success("Volume backup deleted successfully"); + }) + .catch(() => { + toast.error("Error deleting volume backup"); + }); + }} + > + + +
+
+ ); + })} +
+ ) : ( +
+ +

+ No volume backups +

+

+ Create your first volume backup to automate your workflows +

+
+ + +
+
+ )} +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx index 7617b510e..76ab7b6cf 100644 --- a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx +++ b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx @@ -199,7 +199,7 @@ const RestoreBackupSchema = z } }); -const formatBytes = (bytes: number): string => { +export const formatBytes = (bytes: number): string => { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; @@ -415,7 +415,7 @@ export const RestoreBackup = ({ Search Backup Files {field.value && ( - + {field.value} - {field.value || "Search and select a backup file"} + + {field.value || "Search and select a backup file"} + diff --git a/apps/dokploy/drizzle/0101_moaning_blazing_skull.sql b/apps/dokploy/drizzle/0101_moaning_blazing_skull.sql new file mode 100644 index 000000000..91674ee2c --- /dev/null +++ b/apps/dokploy/drizzle/0101_moaning_blazing_skull.sql @@ -0,0 +1,33 @@ +CREATE TABLE "volume_backup" ( + "volumeBackupId" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "volumeName" text NOT NULL, + "prefix" text NOT NULL, + "serviceType" "serviceType" DEFAULT 'application' NOT NULL, + "appName" text NOT NULL, + "serviceName" text, + "turnOff" boolean DEFAULT false NOT NULL, + "cronExpression" text NOT NULL, + "keepLatestCount" integer, + "enabled" boolean, + "applicationId" text, + "postgresId" text, + "mariadbId" text, + "mongoId" text, + "mysqlId" text, + "redisId" text, + "composeId" text, + "createdAt" text NOT NULL, + "destinationId" text NOT NULL +); +--> statement-breakpoint +ALTER TABLE "deployment" ADD COLUMN "volumeBackupId" text;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_applicationId_application_applicationId_fk" FOREIGN KEY ("applicationId") REFERENCES "public"."application"("applicationId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_postgresId_postgres_postgresId_fk" FOREIGN KEY ("postgresId") REFERENCES "public"."postgres"("postgresId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_mariadbId_mariadb_mariadbId_fk" FOREIGN KEY ("mariadbId") REFERENCES "public"."mariadb"("mariadbId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_mongoId_mongo_mongoId_fk" FOREIGN KEY ("mongoId") REFERENCES "public"."mongo"("mongoId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_mysqlId_mysql_mysqlId_fk" FOREIGN KEY ("mysqlId") REFERENCES "public"."mysql"("mysqlId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_redisId_redis_redisId_fk" FOREIGN KEY ("redisId") REFERENCES "public"."redis"("redisId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_composeId_compose_composeId_fk" FOREIGN KEY ("composeId") REFERENCES "public"."compose"("composeId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "volume_backup" ADD CONSTRAINT "volume_backup_destinationId_destination_destinationId_fk" FOREIGN KEY ("destinationId") REFERENCES "public"."destination"("destinationId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "deployment" ADD CONSTRAINT "deployment_volumeBackupId_volume_backup_volumeBackupId_fk" FOREIGN KEY ("volumeBackupId") REFERENCES "public"."volume_backup"("volumeBackupId") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0101_snapshot.json b/apps/dokploy/drizzle/meta/0101_snapshot.json new file mode 100644 index 000000000..c035a435d --- /dev/null +++ b/apps/dokploy/drizzle/meta/0101_snapshot.json @@ -0,0 +1,6122 @@ +{ + "id": "69f87478-2c7c-4e43-ad98-7718bcd91259", + "prevId": "abe187f7-21c6-47c4-9492-038e866ded60", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "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 + }, + "rollbackActive": { + "name": "rollbackActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "cleanCache": { + "name": "cleanCache", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBuildPath": { + "name": "giteaBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isStaticSpa": { + "name": "isStaticSpa", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_giteaId_gitea_giteaId_fk": { + "name": "application_giteaId_gitea_giteaId_fk", + "tableFrom": "application", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "logCleanupCron": { + "name": "logCleanupCron", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0 0 * * *'" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allowImpersonation": { + "name": "allowImpersonation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customCertResolver": { + "name": "customCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "internalPath": { + "name": "internalPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "stripPath": { + "name": "stripPath", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "backupType": { + "name": "backupType", + "type": "backupType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'database'" + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_composeId_compose_composeId_fk": { + "name": "backup_composeId_compose_composeId_fk", + "tableFrom": "backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_userId_user_temp_id_fk": { + "name": "backup_userId_user_temp_id_fk", + "tableFrom": "backup", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "backup_appName_unique": { + "name": "backup_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pid": { + "name": "pid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startedAt": { + "name": "startedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finishedAt": { + "name": "finishedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_scheduleId_schedule_scheduleId_fk": { + "name": "deployment_scheduleId_schedule_scheduleId_fk", + "tableFrom": "deployment", + "tableTo": "schedule", + "columnsFrom": [ + "scheduleId" + ], + "columnsTo": [ + "scheduleId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_backupId_backup_backupId_fk": { + "name": "deployment_backupId_backup_backupId_fk", + "tableFrom": "deployment", + "tableTo": "backup", + "columnsFrom": [ + "backupId" + ], + "columnsTo": [ + "backupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_rollbackId_rollback_rollbackId_fk": { + "name": "deployment_rollbackId_rollback_rollbackId_fk", + "tableFrom": "deployment", + "tableTo": "rollback", + "columnsFrom": [ + "rollbackId" + ], + "columnsTo": [ + "rollbackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_volumeBackupId_volume_backup_volumeBackupId_fk": { + "name": "deployment_volumeBackupId_volume_backup_volumeBackupId_fk", + "tableFrom": "deployment", + "tableTo": "volume_backup", + "columnsFrom": [ + "volumeBackupId" + ], + "columnsTo": [ + "volumeBackupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "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.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "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.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "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'" + }, + "projectId": { + "name": "projectId", + "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_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_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.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.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_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.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "messageThreadId": { + "name": "messageThreadId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitea": { + "name": "gitea", + "schema": "", + "columns": { + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "giteaUrl": { + "name": "giteaUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitea.com'" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'repo,repo:status,read:user,read:org'" + }, + "last_authenticated_at": { + "name": "last_authenticated_at", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "gitea_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitea_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitea", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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.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.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_user_id_user_temp_id_fk": { + "name": "apikey_user_id_user_temp_id_fk", + "tableFrom": "apikey", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.schedule": { + "name": "schedule", + "schema": "", + "columns": { + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shellType": { + "name": "shellType", + "type": "shellType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'bash'" + }, + "scheduleType": { + "name": "scheduleType", + "type": "scheduleType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "script": { + "name": "script", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_applicationId_application_applicationId_fk": { + "name": "schedule_applicationId_application_applicationId_fk", + "tableFrom": "schedule", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_composeId_compose_composeId_fk": { + "name": "schedule_composeId_compose_composeId_fk", + "tableFrom": "schedule", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_serverId_server_serverId_fk": { + "name": "schedule_serverId_server_serverId_fk", + "tableFrom": "schedule", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_userId_user_temp_id_fk": { + "name": "schedule_userId_user_temp_id_fk", + "tableFrom": "schedule", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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.volume_backup": { + "name": "volume_backup", + "schema": "", + "columns": { + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "turnOff": { + "name": "turnOff", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "volume_backup_applicationId_application_applicationId_fk": { + "name": "volume_backup_applicationId_application_applicationId_fk", + "tableFrom": "volume_backup", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_postgresId_postgres_postgresId_fk": { + "name": "volume_backup_postgresId_postgres_postgresId_fk", + "tableFrom": "volume_backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mariadbId_mariadb_mariadbId_fk": { + "name": "volume_backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "volume_backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mongoId_mongo_mongoId_fk": { + "name": "volume_backup_mongoId_mongo_mongoId_fk", + "tableFrom": "volume_backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mysqlId_mysql_mysqlId_fk": { + "name": "volume_backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "volume_backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_redisId_redis_redisId_fk": { + "name": "volume_backup_redisId_redis_redisId_fk", + "tableFrom": "volume_backup", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_composeId_compose_composeId_fk": { + "name": "volume_backup_composeId_compose_composeId_fk", + "tableFrom": "volume_backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_destinationId_destination_destinationId_fk": { + "name": "volume_backup_destinationId_destination_destinationId_fk", + "tableFrom": "volume_backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + "railpack" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "drop" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.backupType": { + "name": "backupType", + "schema": "public", + "values": [ + "database", + "compose" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo", + "web-server" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.publishModeType": { + "name": "publishModeType", + "schema": "public", + "values": [ + "ingress", + "host" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none", + "custom" + ] + }, + "public.triggerType": { + "name": "triggerType", + "schema": "public", + "values": [ + "push", + "tag" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket", + "gitea" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + }, + "public.scheduleType": { + "name": "scheduleType", + "schema": "public", + "values": [ + "application", + "compose", + "server", + "dokploy-server" + ] + }, + "public.shellType": { + "name": "shellType", + "schema": "public", + "values": [ + "bash", + "sh" + ] + } + }, + "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 ecd86c92e..59c3cf125 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -708,6 +708,13 @@ "when": 1751741736144, "tag": "0100_purple_rogue", "breakpoints": true + }, + { + "idx": 101, + "version": "7", + "when": 1751751631943, + "tag": "0101_moaning_blazing_skull", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index c3164e9ec..a1c80d7dd 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -14,6 +14,7 @@ import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowPreviewDeployments } from "@/components/dashboard/application/preview-deployments/show-preview-deployments"; import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { UpdateApplication } from "@/components/dashboard/application/update-application"; +import { ShowVolumeBackups } from "@/components/dashboard/application/volume-backups/show-volume-backups"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-container-monitoring"; @@ -61,7 +62,8 @@ type TabState = | "deployments" | "domains" | "monitoring" - | "preview-deployments"; + | "preview-deployments" + | "volume-backups"; const Service = ( props: InferGetServerSidePropsType, @@ -234,6 +236,9 @@ const Service = ( Preview Deployments Schedules + + Volume Backups + Deployments Logs {((data?.serverId && isCloud) || !data?.server) && ( @@ -328,6 +333,15 @@ const Service = ( /> + +
+ +
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index bdef9a851..0870df097 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -4,6 +4,7 @@ import { ShowDeployments } from "@/components/dashboard/application/deployments/ import { ShowDomains } from "@/components/dashboard/application/domains/show-domains"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; +import { ShowVolumeBackups } from "@/components/dashboard/application/volume-backups/show-volume-backups"; import { AddCommandCompose } from "@/components/dashboard/compose/advanced/add-command"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowGeneralCompose } from "@/components/dashboard/compose/general/show"; @@ -57,7 +58,8 @@ type TabState = | "advanced" | "deployments" | "domains" - | "monitoring"; + | "monitoring" + | "volumeBackups"; const Service = ( props: InferGetServerSidePropsType, @@ -214,10 +216,10 @@ const Service = ( className={cn( "xl:grid xl:w-fit max-md:overflow-y-scroll justify-start", isCloud && data?.serverId - ? "xl:grid-cols-9" + ? "xl:grid-cols-10" : data?.serverId - ? "xl:grid-cols-8" - : "xl:grid-cols-9", + ? "xl:grid-cols-9" + : "xl:grid-cols-10", )} > General @@ -226,6 +228,9 @@ const Service = ( Deployments Backups Schedules + + Volume Backups + Logs {((data?.serverId && isCloud) || !data?.server) && ( Monitoring @@ -255,7 +260,15 @@ const Service = (
- + +
+ +
+
@@ -342,6 +355,7 @@ const Service = (
+
diff --git a/apps/dokploy/server/api/root.ts b/apps/dokploy/server/api/root.ts index cce8768cc..5b6de7344 100644 --- a/apps/dokploy/server/api/root.ts +++ b/apps/dokploy/server/api/root.ts @@ -37,6 +37,7 @@ import { swarmRouter } from "./routers/swarm"; import { userRouter } from "./routers/user"; import { scheduleRouter } from "./routers/schedule"; import { rollbackRouter } from "./routers/rollbacks"; +import { volumeBackupsRouter } from "./routers/volume-backups"; /** * This is the primary router for your server. * @@ -82,6 +83,7 @@ export const appRouter = createTRPCRouter({ organization: organizationRouter, schedule: scheduleRouter, rollback: rollbackRouter, + volumeBackups: volumeBackupsRouter, }); // export type definition of API diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 1b9ebd6a1..cdb2c0fbe 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -32,6 +32,7 @@ import { findProjectById, findServerById, findUserById, + getComposeContainer, loadServices, randomizeComposeFile, randomizeIsolatedDeploymentComposeFile, @@ -241,6 +242,27 @@ export const composeRouter = createTRPCRouter({ } return await loadServices(input.composeId, input.type); }), + loadMountsByService: protectedProcedure + .input( + z.object({ + composeId: z.string().min(1), + serviceName: z.string().min(1), + }), + ) + .query(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to load this compose", + }); + } + const container = await getComposeContainer(compose, input.serviceName); + const mounts = container?.Mounts.filter( + (mount) => mount.Type === "volume" && mount.Source !== "", + ); + return mounts; + }), fetchSourceType: protectedProcedure .input(apiFindCompose) .mutation(async ({ input, ctx }) => { diff --git a/apps/dokploy/server/api/routers/mount.ts b/apps/dokploy/server/api/routers/mount.ts index 9a189f614..7add3ab16 100644 --- a/apps/dokploy/server/api/routers/mount.ts +++ b/apps/dokploy/server/api/routers/mount.ts @@ -7,10 +7,13 @@ import { import { createMount, deleteMount, + findApplicationById, findMountById, + getServiceContainer, updateMount, } from "@dokploy/server"; import { createTRPCRouter, protectedProcedure } from "../trpc"; +import { z } from "zod"; export const mountRouter = createTRPCRouter({ create: protectedProcedure @@ -33,4 +36,14 @@ export const mountRouter = createTRPCRouter({ .mutation(async ({ input }) => { return await updateMount(input.mountId, input); }), + allNamedByApplicationId: protectedProcedure + .input(z.object({ applicationId: z.string().min(1) })) + .query(async ({ input }) => { + const app = await findApplicationById(input.applicationId); + const container = await getServiceContainer(app.appName, app.serverId); + const mounts = container?.Mounts.filter( + (mount) => mount.Type === "volume" && mount.Source !== "", + ); + return mounts; + }), }); diff --git a/apps/dokploy/server/api/routers/volume-backups.ts b/apps/dokploy/server/api/routers/volume-backups.ts new file mode 100644 index 000000000..47e7cb6ec --- /dev/null +++ b/apps/dokploy/server/api/routers/volume-backups.ts @@ -0,0 +1,218 @@ +import { + IS_CLOUD, + updateVolumeBackup, + removeVolumeBackup, + createVolumeBackup, + runVolumeBackup, + findVolumeBackupById, + restoreVolume, + scheduleVolumeBackup, + removeVolumeBackupJob, +} from "@dokploy/server"; +import { + createVolumeBackupSchema, + updateVolumeBackupSchema, + volumeBackups, +} from "@dokploy/server/db/schema"; +import { z } from "zod"; +import { createTRPCRouter, protectedProcedure } from "../trpc"; +import { db } from "@dokploy/server/db"; +import { eq } from "drizzle-orm"; +import { observable } from "@trpc/server/observable"; +import { + execAsyncRemote, + execAsyncStream, +} from "@dokploy/server/utils/process/execAsync"; +import { removeJob, schedule, updateJob } from "@/server/utils/backup"; +import { TRPCError } from "@trpc/server"; + +export const volumeBackupsRouter = createTRPCRouter({ + list: protectedProcedure + .input( + z.object({ + id: z.string().min(1), + volumeBackupType: z.enum([ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose", + ]), + }), + ) + .query(async ({ input }) => { + return await db.query.volumeBackups.findMany({ + where: eq(volumeBackups[`${input.volumeBackupType}Id`], input.id), + with: { + application: true, + postgres: true, + mysql: true, + mariadb: true, + mongo: true, + redis: true, + compose: true, + }, + }); + }), + create: protectedProcedure + .input(createVolumeBackupSchema) + .mutation(async ({ input }) => { + const newVolumeBackup = await createVolumeBackup(input); + + if (newVolumeBackup?.enabled) { + if (IS_CLOUD) { + await schedule({ + cronSchedule: newVolumeBackup.cronExpression, + volumeBackupId: newVolumeBackup.volumeBackupId, + type: "volume-backup", + }); + } else { + await scheduleVolumeBackup(newVolumeBackup.volumeBackupId); + } + } + return newVolumeBackup; + }), + one: protectedProcedure + .input( + z.object({ + volumeBackupId: z.string().min(1), + }), + ) + .query(async ({ input }) => { + return await findVolumeBackupById(input.volumeBackupId); + }), + delete: protectedProcedure + .input( + z.object({ + volumeBackupId: z.string().min(1), + }), + ) + .mutation(async ({ input }) => { + return await removeVolumeBackup(input.volumeBackupId); + }), + update: protectedProcedure + .input(updateVolumeBackupSchema) + .mutation(async ({ input }) => { + const updatedVolumeBackup = await updateVolumeBackup( + input.volumeBackupId, + input, + ); + + if (!updatedVolumeBackup) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Volume backup not found", + }); + } + + if (IS_CLOUD) { + if (updatedVolumeBackup.enabled) { + await updateJob({ + cronSchedule: updatedVolumeBackup.cronExpression, + volumeBackupId: updatedVolumeBackup.volumeBackupId, + type: "volume-backup", + }); + } else { + await removeJob({ + cronSchedule: updatedVolumeBackup.cronExpression, + volumeBackupId: updatedVolumeBackup.volumeBackupId, + type: "volume-backup", + }); + } + } else { + if (updatedVolumeBackup?.enabled) { + removeVolumeBackupJob(updatedVolumeBackup.volumeBackupId); + scheduleVolumeBackup(updatedVolumeBackup.volumeBackupId); + } else { + removeVolumeBackupJob(updatedVolumeBackup.volumeBackupId); + } + } + return updatedVolumeBackup; + }), + + runManually: protectedProcedure + .input(z.object({ volumeBackupId: z.string().min(1) })) + .mutation(async ({ input }) => { + try { + return await runVolumeBackup(input.volumeBackupId); + } catch (error) { + console.error(error); + return false; + } + }), + restoreVolumeBackupWithLogs: protectedProcedure + .meta({ + openapi: { + enabled: false, + path: "/restore-volume-backup-with-logs", + method: "POST", + override: true, + }, + }) + .input( + z.object({ + backupFileName: z.string().min(1), + destinationId: z.string().min(1), + volumeName: z.string().min(1), + id: z.string().min(1), + serviceType: z.enum(["application", "compose"]), + serverId: z.string().optional(), + }), + ) + .subscription(async ({ input }) => { + return observable((emit) => { + const runRestore = async () => { + try { + emit.next("🚀 Starting volume restore process..."); + emit.next(`📂 Backup File: ${input.backupFileName}`); + emit.next(`🔧 Volume Name: ${input.volumeName}`); + emit.next(`🏷️ Service Type: ${input.serviceType}`); + emit.next(""); // Empty line for better readability + + // Generate the restore command + const restoreCommand = await restoreVolume( + input.id, + input.destinationId, + input.volumeName, + input.backupFileName, + input.serverId || "", + input.serviceType, + ); + + emit.next("📋 Generated restore command:"); + emit.next("▶️ Executing restore..."); + emit.next(""); // Empty line + + // Execute the restore command with real-time output + if (input.serverId) { + emit.next(`🌐 Executing on remote server: ${input.serverId}`); + await execAsyncRemote(input.serverId, restoreCommand, (data) => { + emit.next(data); + }); + } else { + emit.next("🖥️ Executing on local server"); + await execAsyncStream(restoreCommand, (data) => { + emit.next(data); + }); + } + + emit.next(""); + emit.next("✅ Volume restore completed successfully!"); + emit.next( + "🎉 All containers/services have been restarted with the restored volume.", + ); + } catch { + emit.next(""); + emit.next("❌ Volume restore failed!"); + } finally { + emit.complete(); + } + }; + + // Start the restore process + runRestore(); + }); + }), +}); diff --git a/apps/dokploy/server/server.ts b/apps/dokploy/server/server.ts index 148edda0d..500a701ae 100644 --- a/apps/dokploy/server/server.ts +++ b/apps/dokploy/server/server.ts @@ -7,6 +7,7 @@ import { createDefaultTraefikConfig, initCronJobs, initSchedules, + initVolumeBackupsCronJobs, initializeNetwork, sendDokployRestartNotifications, setupDirectories, @@ -51,6 +52,7 @@ void app.prepare().then(async () => { await migration(); await initCronJobs(); await initSchedules(); + await initVolumeBackupsCronJobs(); await sendDokployRestartNotifications(); } diff --git a/apps/dokploy/server/utils/backup.ts b/apps/dokploy/server/utils/backup.ts index cf0b6c224..9263ecba8 100644 --- a/apps/dokploy/server/utils/backup.ts +++ b/apps/dokploy/server/utils/backup.ts @@ -19,6 +19,11 @@ type QueueJob = type: "schedule"; cronSchedule: string; scheduleId: string; + } + | { + type: "volume-backup"; + cronSchedule: string; + volumeBackupId: string; }; export const schedule = async (job: QueueJob) => { try { diff --git a/apps/schedules/src/index.ts b/apps/schedules/src/index.ts index 7ab2b98c5..af8ad8ff9 100644 --- a/apps/schedules/src/index.ts +++ b/apps/schedules/src/index.ts @@ -61,6 +61,12 @@ app.post("/update-backup", zValidator("json", jobQueueSchema), async (c) => { type: "schedule", cronSchedule: job.pattern, }); + } else if (data.type === "volume-backup") { + result = await removeJob({ + volumeBackupId: data.volumeBackupId, + type: "volume-backup", + cronSchedule: job.pattern, + }); } logger.info({ result }, "Job removed"); } diff --git a/apps/schedules/src/queue.ts b/apps/schedules/src/queue.ts index 5a1efc058..ebc9fa32a 100644 --- a/apps/schedules/src/queue.ts +++ b/apps/schedules/src/queue.ts @@ -42,6 +42,12 @@ export const scheduleJob = (job: QueueJob) => { pattern: job.cronSchedule, }, }); + } else if (job.type === "volume-backup") { + jobQueue.add(job.volumeBackupId, job, { + repeat: { + pattern: job.cronSchedule, + }, + }); } }; @@ -67,6 +73,13 @@ export const removeJob = async (data: QueueJob) => { }); return result; } + if (data.type === "volume-backup") { + const { volumeBackupId, cronSchedule } = data; + const result = await jobQueue.removeRepeatable(volumeBackupId, { + pattern: cronSchedule, + }); + return result; + } return false; }; @@ -89,6 +102,10 @@ export const getJobRepeatable = async ( const job = repeatableJobs.find((j) => j.name === scheduleId); return job ? job : null; } - + if (data.type === "volume-backup") { + const { volumeBackupId } = data; + const job = repeatableJobs.find((j) => j.name === volumeBackupId); + return job ? job : null; + } return null; }; diff --git a/apps/schedules/src/schema.ts b/apps/schedules/src/schema.ts index 32b2536bc..ac01e50de 100644 --- a/apps/schedules/src/schema.ts +++ b/apps/schedules/src/schema.ts @@ -16,6 +16,11 @@ export const jobQueueSchema = z.discriminatedUnion("type", [ type: z.literal("schedule"), scheduleId: z.string(), }), + z.object({ + cronSchedule: z.string(), + type: z.literal("volume-backup"), + volumeBackupId: z.string(), + }), ]); export type QueueJob = z.infer; diff --git a/apps/schedules/src/utils.ts b/apps/schedules/src/utils.ts index e65775aee..4d255a2b7 100644 --- a/apps/schedules/src/utils.ts +++ b/apps/schedules/src/utils.ts @@ -5,6 +5,7 @@ import { findBackupById, findScheduleById, findServerById, + findVolumeBackupById, keepLatestNBackups, runCommand, runComposeBackup, @@ -12,9 +13,15 @@ import { runMongoBackup, runMySqlBackup, runPostgresBackup, + runVolumeBackup, } from "@dokploy/server"; import { db } from "@dokploy/server/dist/db"; -import { backups, schedules, server } from "@dokploy/server/dist/db/schema"; +import { + backups, + schedules, + server, + volumeBackups, +} from "@dokploy/server/dist/db/schema"; import { and, eq } from "drizzle-orm"; import { logger } from "./logger.js"; import { scheduleJob } from "./queue.js"; @@ -93,6 +100,12 @@ export const runJobs = async (job: QueueJob) => { if (schedule.enabled) { await runCommand(schedule.scheduleId); } + } else if (job.type === "volume-backup") { + const { volumeBackupId } = job; + const volumeBackup = await findVolumeBackupById(volumeBackupId); + if (volumeBackup.enabled) { + await runVolumeBackup(volumeBackupId); + } } } catch (error) { logger.error(error); @@ -184,4 +197,44 @@ export const initializeJobs = async () => { { Quantity: filteredSchedulesBasedOnServerStatus.length }, "Schedules Initialized", ); + + const volumeBackupsResult = await db.query.volumeBackups.findMany({ + where: eq(volumeBackups.enabled, true), + with: { + application: { + with: { + server: true, + }, + }, + compose: { + with: { + server: true, + }, + }, + }, + }); + + const filteredVolumeBackupsBasedOnServerStatus = volumeBackupsResult.filter( + (volumeBackup) => { + if (volumeBackup.application) { + return volumeBackup.application.server?.serverStatus === "active"; + } + if (volumeBackup.compose) { + return volumeBackup.compose.server?.serverStatus === "active"; + } + }, + ); + + for (const volumeBackup of filteredVolumeBackupsBasedOnServerStatus) { + scheduleJob({ + volumeBackupId: volumeBackup.volumeBackupId, + type: "volume-backup", + cronSchedule: volumeBackup.cronExpression, + }); + } + + logger.info( + { Quantity: filteredVolumeBackupsBasedOnServerStatus.length }, + "Volume Backups Initialized", + ); }; diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 461d40f5e..7ffacc7ca 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -24,5 +24,6 @@ export const paths = (isServer = false) => { MONITORING_PATH: `${BASE_PATH}/monitoring`, REGISTRY_PATH: `${BASE_PATH}/registry`, SCHEDULES_PATH: `${BASE_PATH}/schedules`, + VOLUME_BACKUPS_PATH: `${BASE_PATH}/volume-backups`, }; }; diff --git a/packages/server/src/db/schema/deployment.ts b/packages/server/src/db/schema/deployment.ts index 59e1c8529..fc2da4cb6 100644 --- a/packages/server/src/db/schema/deployment.ts +++ b/packages/server/src/db/schema/deployment.ts @@ -16,6 +16,7 @@ import { previewDeployments } from "./preview-deployments"; import { schedules } from "./schedule"; import { server } from "./server"; import { rollbacks } from "./rollbacks"; +import { volumeBackups } from "./volume-backups"; export const deploymentStatus = pgEnum("deploymentStatus", [ "running", "done", @@ -64,6 +65,10 @@ export const deployments = pgTable("deployment", { (): AnyPgColumn => rollbacks.rollbackId, { onDelete: "cascade" }, ), + volumeBackupId: text("volumeBackupId").references( + (): AnyPgColumn => volumeBackups.volumeBackupId, + { onDelete: "cascade" }, + ), }); export const deploymentsRelations = relations(deployments, ({ one }) => ({ @@ -95,6 +100,10 @@ export const deploymentsRelations = relations(deployments, ({ one }) => ({ fields: [deployments.deploymentId], references: [rollbacks.deploymentId], }), + volumeBackup: one(volumeBackups, { + fields: [deployments.volumeBackupId], + references: [volumeBackups.volumeBackupId], + }), })); const schema = createInsertSchema(deployments, { @@ -179,6 +188,17 @@ export const apiCreateDeploymentSchedule = schema scheduleId: z.string().min(1), }); +export const apiCreateDeploymentVolumeBackup = schema + .pick({ + title: true, + status: true, + logPath: true, + description: true, + }) + .extend({ + volumeBackupId: z.string().min(1), + }); + export const apiFindAllByApplication = schema .pick({ applicationId: true, @@ -216,6 +236,7 @@ export const apiFindAllByType = z "schedule", "previewDeployment", "backup", + "volumeBackup", ]), }) .required(); diff --git a/packages/server/src/db/schema/index.ts b/packages/server/src/db/schema/index.ts index 439e669ec..820f23182 100644 --- a/packages/server/src/db/schema/index.ts +++ b/packages/server/src/db/schema/index.ts @@ -33,3 +33,4 @@ export * from "./ai"; export * from "./account"; export * from "./schedule"; export * from "./rollbacks"; +export * from "./volume-backups"; diff --git a/packages/server/src/db/schema/volume-backups.ts b/packages/server/src/db/schema/volume-backups.ts new file mode 100644 index 000000000..69ab62ca2 --- /dev/null +++ b/packages/server/src/db/schema/volume-backups.ts @@ -0,0 +1,118 @@ +import { relations } from "drizzle-orm"; +import { boolean, integer, pgTable, text } from "drizzle-orm/pg-core"; +import { createInsertSchema } from "drizzle-zod"; +import { nanoid } from "nanoid"; +import { z } from "zod"; +import { serviceType } from "./mount"; +import { applications } from "./application"; +import { mongo } from "./mongo"; +import { mysql } from "./mysql"; +import { redis } from "./redis"; +import { compose } from "./compose"; +import { postgres } from "./postgres"; +import { mariadb } from "./mariadb"; +import { destinations } from "./destination"; +import { deployments } from "./deployment"; +import { generateAppName } from "./utils"; + +export const volumeBackups = pgTable("volume_backup", { + volumeBackupId: text("volumeBackupId") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + name: text("name").notNull(), + volumeName: text("volumeName").notNull(), + prefix: text("prefix").notNull(), + serviceType: serviceType("serviceType").notNull().default("application"), + appName: text("appName") + .notNull() + .$defaultFn(() => generateAppName("volumeBackup")), + serviceName: text("serviceName"), + turnOff: boolean("turnOff").notNull().default(false), + cronExpression: text("cronExpression").notNull(), + keepLatestCount: integer("keepLatestCount"), + enabled: boolean("enabled"), + applicationId: text("applicationId").references( + () => applications.applicationId, + { + onDelete: "cascade", + }, + ), + postgresId: text("postgresId").references(() => postgres.postgresId, { + onDelete: "cascade", + }), + mariadbId: text("mariadbId").references(() => mariadb.mariadbId, { + onDelete: "cascade", + }), + mongoId: text("mongoId").references(() => mongo.mongoId, { + onDelete: "cascade", + }), + mysqlId: text("mysqlId").references(() => mysql.mysqlId, { + onDelete: "cascade", + }), + redisId: text("redisId").references(() => redis.redisId, { + onDelete: "cascade", + }), + composeId: text("composeId").references(() => compose.composeId, { + onDelete: "cascade", + }), + createdAt: text("createdAt") + .notNull() + .$defaultFn(() => new Date().toISOString()), + destinationId: text("destinationId") + .notNull() + .references(() => destinations.destinationId, { onDelete: "cascade" }), +}); + +export type VolumeBackup = typeof volumeBackups.$inferSelect; + +export const volumeBackupsRelations = relations( + volumeBackups, + ({ one, many }) => ({ + application: one(applications, { + fields: [volumeBackups.applicationId], + references: [applications.applicationId], + }), + postgres: one(postgres, { + fields: [volumeBackups.postgresId], + references: [postgres.postgresId], + }), + mariadb: one(mariadb, { + fields: [volumeBackups.mariadbId], + references: [mariadb.mariadbId], + }), + mongo: one(mongo, { + fields: [volumeBackups.mongoId], + references: [mongo.mongoId], + }), + mysql: one(mysql, { + fields: [volumeBackups.mysqlId], + references: [mysql.mysqlId], + }), + redis: one(redis, { + fields: [volumeBackups.redisId], + references: [redis.redisId], + }), + compose: one(compose, { + fields: [volumeBackups.composeId], + references: [compose.composeId], + }), + destination: one(destinations, { + fields: [volumeBackups.destinationId], + references: [destinations.destinationId], + }), + deployments: many(deployments), + }), +); + +export const createVolumeBackupSchema = createInsertSchema(volumeBackups).omit({ + volumeBackupId: true, +}); + +export const updateVolumeBackupSchema = createVolumeBackupSchema.extend({ + volumeBackupId: z.string().min(1), +}); + +export const apiFindOneVolumeBackup = z.object({ + volumeBackupId: z.string().min(1), +}); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index c2938e23e..1c0655f8b 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -10,6 +10,7 @@ export * from "./services/mysql"; export * from "./services/backup"; export * from "./services/cluster"; export * from "./services/settings"; +export * from "./services/volume-backups"; export * from "./services/docker"; export * from "./services/destination"; export * from "./services/deployment"; @@ -132,5 +133,6 @@ export { export * from "./utils/schedules/utils"; export * from "./utils/schedules/index"; +export * from "./utils/volume-backups/index"; export * from "./lib/logger"; diff --git a/packages/server/src/services/deployment.ts b/packages/server/src/services/deployment.ts index fb91c1c26..12fdff5b9 100644 --- a/packages/server/src/services/deployment.ts +++ b/packages/server/src/services/deployment.ts @@ -9,6 +9,7 @@ import { type apiCreateDeploymentPreview, type apiCreateDeploymentSchedule, type apiCreateDeploymentServer, + type apiCreateDeploymentVolumeBackup, deployments, } from "@dokploy/server/db/schema"; import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory"; @@ -32,6 +33,7 @@ import { } from "./preview-deployment"; import { findScheduleById } from "./schedule"; import { removeRollbackById } from "./rollbacks"; +import { findVolumeBackupById } from "./volume-backups"; export type Deployment = typeof deployments.$inferSelect; @@ -458,6 +460,91 @@ export const createDeploymentSchedule = async ( } }; +export const createDeploymentVolumeBackup = async ( + deployment: Omit< + typeof apiCreateDeploymentVolumeBackup._type, + "deploymentId" | "createdAt" | "status" | "logPath" + >, +) => { + const volumeBackup = await findVolumeBackupById(deployment.volumeBackupId); + + try { + const serverId = + volumeBackup.application?.serverId || volumeBackup.compose?.serverId; + await removeLastTenDeployments( + deployment.volumeBackupId, + "volumeBackup", + serverId, + ); + const { VOLUME_BACKUPS_PATH } = paths(!!serverId); + const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss"); + const fileName = `${volumeBackup.appName}-${formattedDateTime}.log`; + const logFilePath = path.join( + VOLUME_BACKUPS_PATH, + volumeBackup.appName, + fileName, + ); + + if (serverId) { + const server = await findServerById(serverId); + + const command = ` + mkdir -p ${VOLUME_BACKUPS_PATH}/${volumeBackup.appName}; + echo "Initializing volume backup" >> ${logFilePath}; + `; + + await execAsyncRemote(server.serverId, command); + } else { + await fsPromises.mkdir( + path.join(VOLUME_BACKUPS_PATH, volumeBackup.appName), + { + recursive: true, + }, + ); + await fsPromises.writeFile(logFilePath, "Initializing volume backup\n"); + } + + const deploymentCreate = await db + .insert(deployments) + .values({ + volumeBackupId: deployment.volumeBackupId, + title: deployment.title || "Deployment", + status: "running", + logPath: logFilePath, + description: deployment.description || "", + startedAt: new Date().toISOString(), + }) + .returning(); + if (deploymentCreate.length === 0 || !deploymentCreate[0]) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error creating the deployment", + }); + } + return deploymentCreate[0]; + } catch (error) { + console.log(error); + await db + .insert(deployments) + .values({ + volumeBackupId: deployment.volumeBackupId, + title: deployment.title || "Deployment", + status: "error", + logPath: "", + description: deployment.description || "", + errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`, + startedAt: new Date().toISOString(), + finishedAt: new Date().toISOString(), + }) + .returning(); + + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error creating the deployment", + }); + } +}; + export const removeDeployment = async (deploymentId: string) => { try { const deployment = await db @@ -492,7 +579,8 @@ const getDeploymentsByType = async ( | "server" | "schedule" | "previewDeployment" - | "backup", + | "backup" + | "volumeBackup", ) => { const deploymentList = await db.query.deployments.findMany({ where: eq(deployments[`${type}Id`], id), @@ -524,7 +612,8 @@ const removeLastTenDeployments = async ( | "server" | "schedule" | "previewDeployment" - | "backup", + | "backup" + | "volumeBackup", serverId?: string | null, ) => { const deploymentList = await getDeploymentsByType(id, type); diff --git a/packages/server/src/services/volume-backups.ts b/packages/server/src/services/volume-backups.ts new file mode 100644 index 000000000..1b50e1465 --- /dev/null +++ b/packages/server/src/services/volume-backups.ts @@ -0,0 +1,64 @@ +import { eq } from "drizzle-orm"; +import { + type createVolumeBackupSchema, + type updateVolumeBackupSchema, + volumeBackups, +} from "../db/schema"; +import { db } from "../db"; +import { TRPCError } from "@trpc/server"; +import type { z } from "zod"; + +export const findVolumeBackupById = async (volumeBackupId: string) => { + const volumeBackup = await db.query.volumeBackups.findFirst({ + where: eq(volumeBackups.volumeBackupId, volumeBackupId), + with: { + application: true, + postgres: true, + mysql: true, + mariadb: true, + mongo: true, + redis: true, + compose: true, + destination: true, + }, + }); + + if (!volumeBackup) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Volume backup not found", + }); + } + + return volumeBackup; +}; + +export const createVolumeBackup = async ( + volumeBackup: z.infer, +) => { + const newVolumeBackup = await db + .insert(volumeBackups) + .values(volumeBackup) + .returning() + .then((e) => e[0]); + + return newVolumeBackup; +}; + +export const removeVolumeBackup = async (volumeBackupId: string) => { + await db + .delete(volumeBackups) + .where(eq(volumeBackups.volumeBackupId, volumeBackupId)); +}; + +export const updateVolumeBackup = async ( + volumeBackupId: string, + volumeBackup: z.infer, +) => { + return await db + .update(volumeBackups) + .set(volumeBackup) + .where(eq(volumeBackups.volumeBackupId, volumeBackupId)) + .returning() + .then((e) => e[0]); +}; diff --git a/packages/server/src/setup/config-paths.ts b/packages/server/src/setup/config-paths.ts index 95f2d30d1..3d61569e5 100644 --- a/packages/server/src/setup/config-paths.ts +++ b/packages/server/src/setup/config-paths.ts @@ -19,6 +19,7 @@ export const setupDirectories = () => { MONITORING_PATH, SSH_PATH, SCHEDULES_PATH, + VOLUME_BACKUPS_PATH, } = paths(); const directories = [ BASE_PATH, @@ -30,6 +31,7 @@ export const setupDirectories = () => { CERTIFICATES_PATH, MONITORING_PATH, SCHEDULES_PATH, + VOLUME_BACKUPS_PATH, ]; for (const dir of directories) { diff --git a/packages/server/src/utils/volume-backups/backup.ts b/packages/server/src/utils/volume-backups/backup.ts new file mode 100644 index 000000000..46a15657f --- /dev/null +++ b/packages/server/src/utils/volume-backups/backup.ts @@ -0,0 +1,91 @@ +import type { findVolumeBackupById } from "@dokploy/server/services/volume-backups"; +import { normalizeS3Path } from "../backups/utils"; +import { getS3Credentials } from "../backups/utils"; +import path from "node:path"; +import { paths } from "@dokploy/server/constants"; +import { findComposeById } from "@dokploy/server/services/compose"; + +export const backupVolume = async ( + volumeBackup: Awaited>, +) => { + const { serviceType, volumeName, turnOff, prefix } = volumeBackup; + const serverId = + volumeBackup.application?.serverId || volumeBackup.compose?.serverId; + const { VOLUME_BACKUPS_PATH } = paths(!!serverId); + const destination = volumeBackup.destination; + const backupFileName = `${volumeName}-${new Date().toISOString()}.tar`; + const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`; + const rcloneFlags = getS3Credentials(volumeBackup.destination); + const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`; + const volumeBackupPath = path.join(VOLUME_BACKUPS_PATH, volumeBackup.appName); + + const rcloneCommand = `rclone copyto ${rcloneFlags.join(" ")} "${volumeBackupPath}/${backupFileName}" "${rcloneDestination}"`; + + const baseCommand = ` + set -e + echo "Volume name: ${volumeName}" + echo "Backup file name: ${backupFileName}" + echo "Turning off volume backup: ${turnOff ? "Yes" : "No"}" + echo "Starting volume backup" + echo "Dir: ${volumeBackupPath}" + docker run --rm \ + -v ${volumeName}:/volume_data \ + -v ${volumeBackupPath}:/backup \ + ubuntu \ + bash -c "cd /volume_data && tar cvf /backup/${backupFileName} ." + echo "Volume backup done ✅" + echo "Starting upload to S3..." + ${rcloneCommand} + echo "Upload to S3 done ✅" + `; + + if (!turnOff) { + return baseCommand; + } + + if (serviceType === "application") { + return ` + echo "Stopping application to 0 replicas" + ACTUAL_REPLICAS=$(docker service inspect ${volumeBackup.application?.appName} --format "{{.Spec.Mode.Replicated.Replicas}}") + echo "Actual replicas: $ACTUAL_REPLICAS" + docker service scale ${volumeBackup.application?.appName}=0 + ${baseCommand} + echo "Starting application to $ACTUAL_REPLICAS replicas" + docker service scale ${volumeBackup.application?.appName}=$ACTUAL_REPLICAS + `; + } + if (serviceType === "compose") { + const compose = await findComposeById( + volumeBackup.compose?.composeId || "", + ); + let stopCommand = ""; + let startCommand = ""; + + if (compose.composeType === "stack") { + stopCommand = ` + echo "Stopping compose to 0 replicas" + echo "Service name: ${compose.appName}_${volumeBackup.serviceName}" + ACTUAL_REPLICAS=$(docker service inspect ${compose.appName}_${volumeBackup.serviceName} --format "{{.Spec.Mode.Replicated.Replicas}}") + echo "Actual replicas: $ACTUAL_REPLICAS" + docker service scale ${compose.appName}_${volumeBackup.serviceName}=0`; + startCommand = ` + echo "Starting compose to $ACTUAL_REPLICAS replicas" + docker service scale ${compose.appName}_${volumeBackup.serviceName}=$ACTUAL_REPLICAS`; + } else { + stopCommand = ` + echo "Stopping compose container" + ID=$(docker ps -q --filter "label=com.docker.compose.project=${compose.appName}" --filter "label=com.docker.compose.service=${volumeBackup.serviceName}") + docker stop $ID`; + startCommand = ` + echo "Starting compose container" + docker start $ID + echo "Compose container started" + `; + } + return ` + ${stopCommand} + ${baseCommand} + ${startCommand} + `; + } +}; diff --git a/packages/server/src/utils/volume-backups/index.ts b/packages/server/src/utils/volume-backups/index.ts new file mode 100644 index 000000000..eb80b09a7 --- /dev/null +++ b/packages/server/src/utils/volume-backups/index.ts @@ -0,0 +1,30 @@ +export * from "./backup"; +export * from "./restore"; +export * from "./utils"; +import { volumeBackups } from "@dokploy/server/db/schema"; +import { eq } from "drizzle-orm"; +import { db } from "../../db/index"; +import { scheduleVolumeBackup } from "./utils"; + +export const initVolumeBackupsCronJobs = async () => { + console.log("Setting up volume backups cron jobs...."); + try { + const volumeBackupsResult = await db.query.volumeBackups.findMany({ + where: eq(volumeBackups.enabled, true), + with: { + application: true, + compose: true, + }, + }); + + console.log(`Initializing ${volumeBackupsResult.length} volume backups`); + for (const volumeBackup of volumeBackupsResult) { + scheduleVolumeBackup(volumeBackup.volumeBackupId); + console.log( + `Initialized volume backup: ${volumeBackup.name} ${volumeBackup.serviceType} ✅`, + ); + } + } catch (error) { + console.log(`Error initializing volume backups: ${error}`); + } +}; diff --git a/packages/server/src/utils/volume-backups/restore.ts b/packages/server/src/utils/volume-backups/restore.ts new file mode 100644 index 000000000..b30e585ea --- /dev/null +++ b/packages/server/src/utils/volume-backups/restore.ts @@ -0,0 +1,126 @@ +import { + findApplicationById, + findComposeById, + findDestinationById, + getS3Credentials, + paths, +} from "../.."; +import path from "node:path"; + +export const restoreVolume = async ( + id: string, + destinationId: string, + volumeName: string, + backupFileName: string, + serverId: string, + serviceType: "application" | "compose", +) => { + const destination = await findDestinationById(destinationId); + const { VOLUME_BACKUPS_PATH } = paths(!!serverId); + const volumeBackupPath = path.join(VOLUME_BACKUPS_PATH, volumeName); + const rcloneFlags = getS3Credentials(destination); + const bucketPath = `:s3:${destination.bucket}`; + const backupPath = `${bucketPath}/${backupFileName}`; + + // Command to download backup file from S3 + const downloadCommand = `rclone copyto ${rcloneFlags.join(" ")} "${backupPath}" "${volumeBackupPath}/${backupFileName}"`; + + // Base restore command that creates the volume and restores data + const baseRestoreCommand = ` + set -e + echo "Volume name: ${volumeName}" + echo "Backup file name: ${backupFileName}" + echo "Volume backup path: ${volumeBackupPath}" + echo "Downloading backup from S3..." + mkdir -p ${volumeBackupPath} + ${downloadCommand} + echo "Download completed ✅" + echo "Creating new volume and restoring data..." + docker run --rm \ + -v ${volumeName}:/volume_data \ + -v ${volumeBackupPath}:/backup \ + ubuntu \ + bash -c "cd /volume_data && tar xvf /backup/${backupFileName} ." + echo "Volume restore completed ✅" + `; + + // Function to check if volume exists and get containers using it + const checkVolumeCommand = ` + # Check if volume exists + VOLUME_EXISTS=$(docker volume ls -q --filter name="^${volumeName}$" | wc -l) + echo "Volume exists: $VOLUME_EXISTS" + + if [ "$VOLUME_EXISTS" = "0" ]; then + echo "Volume doesn't exist, proceeding with direct restore" + ${baseRestoreCommand} + else + echo "Volume exists, checking for containers using it (including stopped ones)..." + + # Get ALL containers (running and stopped) using this volume - much simpler with native filter! + CONTAINERS_USING_VOLUME=$(docker ps -a --filter "volume=${volumeName}" --format "{{.ID}}|{{.Names}}|{{.State}}|{{.Labels}}") + + if [ -z "$CONTAINERS_USING_VOLUME" ]; then + echo "Volume exists but no containers are using it" + echo "Removing existing volume and proceeding with restore" + docker volume rm ${volumeName} --force + ${baseRestoreCommand} + else + echo "" + echo "⚠️ WARNING: Cannot restore volume as it is currently in use!" + echo "" + echo "📋 The following containers are using volume '${volumeName}':" + echo "" + + echo "$CONTAINERS_USING_VOLUME" | while IFS='|' read container_id container_name container_state labels; do + echo " 🐳 Container: $container_name ($container_id)" + echo " Status: $container_state" + + # Determine container type + if echo "$labels" | grep -q "com.docker.swarm.service.name="; then + SERVICE_NAME=$(echo "$labels" | grep -o "com.docker.swarm.service.name=[^,]*" | cut -d'=' -f2) + echo " Type: Docker Swarm Service ($SERVICE_NAME)" + elif echo "$labels" | grep -q "com.docker.compose.project="; then + PROJECT_NAME=$(echo "$labels" | grep -o "com.docker.compose.project=[^,]*" | cut -d'=' -f2) + echo " Type: Docker Compose ($PROJECT_NAME)" + else + echo " Type: Regular Container" + fi + echo "" + done + + echo "" + echo "🔧 To restore this volume, please:" + echo " 1. Stop all containers/services using this volume" + echo " 2. Remove the existing volume: docker volume rm ${volumeName}" + echo " 3. Run the restore operation again" + echo "" + echo "❌ Volume restore aborted - volume is in use" + + exit 1 + fi + fi + `; + + if (serviceType === "application") { + const application = await findApplicationById(id); + return ` + echo "=== VOLUME RESTORE FOR APPLICATION ===" + echo "Application: ${application.appName}" + ${checkVolumeCommand} + `; + } + + if (serviceType === "compose") { + const compose = await findComposeById(id); + + return ` + echo "=== VOLUME RESTORE FOR COMPOSE ===" + echo "Compose: ${compose.appName}" + echo "Compose Type: ${compose.composeType}" + ${checkVolumeCommand} + `; + } + + // Fallback for unknown service types + return checkVolumeCommand; +}; diff --git a/packages/server/src/utils/volume-backups/utils.ts b/packages/server/src/utils/volume-backups/utils.ts new file mode 100644 index 000000000..69affad34 --- /dev/null +++ b/packages/server/src/utils/volume-backups/utils.ts @@ -0,0 +1,48 @@ +import { findVolumeBackupById } from "@dokploy/server/services/volume-backups"; +import { + createDeploymentVolumeBackup, + execAsync, + execAsyncRemote, + updateDeploymentStatus, +} from "../.."; +import { backupVolume } from "./backup"; +import { scheduleJob, scheduledJobs } from "node-schedule"; + +export const scheduleVolumeBackup = async (volumeBackupId: string) => { + const volumeBackup = await findVolumeBackupById(volumeBackupId); + scheduleJob(volumeBackupId, volumeBackup.cronExpression, async () => { + await runVolumeBackup(volumeBackupId); + }); +}; + +export const removeVolumeBackupJob = async (volumeBackupId: string) => { + const currentJob = scheduledJobs[volumeBackupId]; + currentJob?.cancel(); +}; + +export const runVolumeBackup = async (volumeBackupId: string) => { + const volumeBackup = await findVolumeBackupById(volumeBackupId); + const serverId = + volumeBackup.application?.serverId || volumeBackup.compose?.serverId; + const deployment = await createDeploymentVolumeBackup({ + volumeBackupId: volumeBackup.volumeBackupId, + title: "Volume Backup", + description: "Volume Backup", + }); + + try { + const command = await backupVolume(volumeBackup); + + const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; + if (serverId) { + await execAsyncRemote(serverId, commandWithLog); + } else { + await execAsync(commandWithLog); + } + + await updateDeploymentStatus(deployment.deploymentId, "done"); + } catch (error) { + await updateDeploymentStatus(deployment.deploymentId, "error"); + console.error(error); + } +};