diff --git a/apps/dokploy/__test__/cluster/upload.test.ts b/apps/dokploy/__test__/cluster/upload.test.ts index ca95cf6630..1ccb9e22d6 100644 --- a/apps/dokploy/__test__/cluster/upload.test.ts +++ b/apps/dokploy/__test__/cluster/upload.test.ts @@ -206,4 +206,38 @@ describe("getRegistryTag", () => { expect(result).toBe("docker.io/myuser/repo"); }); }); + + describe("special characters in username", () => { + it("should handle Harbor robot account username with $ (e.g. robot$library+dokploy)", () => { + const registry = createMockRegistry({ + username: "robot$library+dokploy", + }); + const result = getRegistryTag(registry, "nginx"); + expect(result).toBe("docker.io/robot$library+dokploy/nginx"); + }); + + it("should handle username with $ and other special characters", () => { + const registry = createMockRegistry({ + username: "robot$test+app", + }); + const result = getRegistryTag(registry, "myapp:latest"); + expect(result).toBe("docker.io/robot$test+app/myapp:latest"); + }); + + it("should handle username with multiple $ symbols", () => { + const registry = createMockRegistry({ + username: "user$name$test", + }); + const result = getRegistryTag(registry, "app"); + expect(result).toBe("docker.io/user$name$test/app"); + }); + + it("should handle username with + and - symbols", () => { + const registry = createMockRegistry({ + username: "robot+test-user", + }); + const result = getRegistryTag(registry, "nginx:latest"); + expect(result).toBe("docker.io/robot+test-user/nginx:latest"); + }); + }); }); diff --git a/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts b/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts index 27e696b207..097c916ea5 100644 --- a/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts +++ b/apps/dokploy/__test__/compose/domain/host-rule-format.test.ts @@ -1,7 +1,7 @@ import type { Domain } from "@dokploy/server"; import { createDomainLabels } from "@dokploy/server"; -import { parse, stringify } from "yaml"; import { describe, expect, it } from "vitest"; +import { parse, stringify } from "yaml"; /** * Regression tests for Traefik Host rule label format. diff --git a/apps/dokploy/__test__/templates/helpers.template.test.ts b/apps/dokploy/__test__/templates/helpers.template.test.ts index 3ae92ae20c..f2af2717b5 100644 --- a/apps/dokploy/__test__/templates/helpers.template.test.ts +++ b/apps/dokploy/__test__/templates/helpers.template.test.ts @@ -161,6 +161,50 @@ describe("helpers functions", () => { }); }); + describe("Empty string variables", () => { + it("should replace variables with empty string values correctly", () => { + const variables = { + smtp_username: "", + smtp_password: "", + non_empty: "value", + }; + + const result1 = processValue("${smtp_username}", variables, mockSchema); + expect(result1).toBe(""); + + const result2 = processValue("${smtp_password}", variables, mockSchema); + expect(result2).toBe(""); + + const result3 = processValue("${non_empty}", variables, mockSchema); + expect(result3).toBe("value"); + }); + + it("should not replace undefined variables", () => { + const variables = { + defined_var: "", + }; + + const result = processValue("${undefined_var}", variables, mockSchema); + expect(result).toBe("${undefined_var}"); + }); + + it("should handle mixed empty and non-empty variables in template", () => { + const variables = { + smtp_address: "smtp.example.com", + smtp_port: "2525", + smtp_username: "", + smtp_password: "", + }; + + const template = + "SMTP_ADDRESS=${smtp_address} SMTP_PORT=${smtp_port} SMTP_USERNAME=${smtp_username} SMTP_PASSWORD=${smtp_password}"; + const result = processValue(template, variables, mockSchema); + expect(result).toBe( + "SMTP_ADDRESS=smtp.example.com SMTP_PORT=2525 SMTP_USERNAME= SMTP_PASSWORD=", + ); + }); + }); + describe("${jwt}", () => { it("should generate a JWT string", () => { const jwt = processValue("${jwt}", {}, mockSchema); diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index f35e8132cc..b422279ca6 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -5,21 +5,27 @@ vi.mock("node:fs", () => ({ default: fs, })); -import type { FileConfig, User } from "@dokploy/server"; +import type { FileConfig } from "@dokploy/server"; import { createDefaultServerTraefikConfig, loadOrCreateConfig, updateServerTraefik, } from "@dokploy/server"; +import type { webServerSettings } from "@dokploy/server/db/schema"; import { beforeEach, expect, test, vi } from "vitest"; -const baseAdmin: User = { +type WebServerSettings = typeof webServerSettings.$inferSelect; + +const baseSettings: WebServerSettings = { + id: "", https: false, - enablePaidFeatures: false, - allowImpersonation: false, - role: "user", - firstName: "", - lastName: "", + certificateType: "none", + host: null, + serverIp: null, + letsEncryptEmail: null, + sshPrivateKey: null, + enableDockerCleanup: false, + logCleanupCron: null, metricsConfig: { containers: { refreshRate: 20, @@ -45,29 +51,8 @@ const baseAdmin: User = { cleanupCacheApplications: false, cleanupCacheOnCompose: false, cleanupCacheOnPreviews: false, - createdAt: new Date(), - serverIp: null, - certificateType: "none", - host: null, - letsEncryptEmail: null, - sshPrivateKey: null, - enableDockerCleanup: false, - logCleanupCron: null, - serversQuantity: 0, - stripeCustomerId: "", - stripeSubscriptionId: "", - banExpires: new Date(), - banned: true, - banReason: "", - email: "", - expirationDate: "", - id: "", - isRegistered: false, - createdAt2: new Date().toISOString(), - emailVerified: false, - image: "", + createdAt: null, updatedAt: new Date(), - twoFactorEnabled: false, }; beforeEach(() => { @@ -85,7 +70,7 @@ test("Should read the configuration file", () => { test("Should apply redirect-to-https", () => { updateServerTraefik( { - ...baseAdmin, + ...baseSettings, https: true, certificateType: "letsencrypt", }, @@ -100,7 +85,7 @@ test("Should apply redirect-to-https", () => { }); test("Should change only host when no certificate", () => { - updateServerTraefik(baseAdmin, "example.com"); + updateServerTraefik(baseSettings, "example.com"); const config: FileConfig = loadOrCreateConfig("dokploy"); @@ -110,7 +95,7 @@ test("Should change only host when no certificate", () => { test("Should not touch config without host", () => { const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); - updateServerTraefik(baseAdmin, null); + updateServerTraefik(baseSettings, null); const config: FileConfig = loadOrCreateConfig("dokploy"); @@ -119,11 +104,14 @@ test("Should not touch config without host", () => { test("Should remove websecure if https rollback to http", () => { updateServerTraefik( - { ...baseAdmin, certificateType: "letsencrypt" }, + { ...baseSettings, certificateType: "letsencrypt" }, "example.com", ); - updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com"); + updateServerTraefik( + { ...baseSettings, certificateType: "none" }, + "example.com", + ); const config: FileConfig = loadOrCreateConfig("dokploy"); diff --git a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx index 3beedcdbcb..aea30e49b6 100644 --- a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx @@ -21,7 +21,10 @@ import { FormLabel, FormMessage, } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; +import { + createConverter, + NumberInputWithSteps, +} from "@/components/ui/number-input"; import { Tooltip, TooltipContent, @@ -30,6 +33,23 @@ import { } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; +const CPU_STEP = 0.25; +const MEMORY_STEP_MB = 256; + +const formatNumber = (value: number, decimals = 2): string => + Number.isInteger(value) ? String(value) : value.toFixed(decimals); + +const cpuConverter = createConverter(1_000_000_000, (cpu) => + cpu <= 0 ? "" : `${formatNumber(cpu)} CPU`, +); + +const memoryConverter = createConverter(1024 * 1024, (mb) => { + if (mb <= 0) return ""; + return mb >= 1024 + ? `${formatNumber(mb / 1024)} GB` + : `${formatNumber(mb)} MB`; +}); + const addResourcesSchema = z.object({ memoryReservation: z.string().optional(), cpuLimit: z.string().optional(), @@ -51,6 +71,7 @@ interface Props { } type AddResources = z.infer; + export const ShowResources = ({ id, type }: Props) => { const queryMap = { postgres: () => @@ -163,16 +184,20 @@ export const ShowResources = ({ id, type }: Props) => {

Memory hard limit in bytes. Example: 1GB = - 1073741824 bytes + 1073741824 bytes. Use +/- buttons to adjust by + 256 MB.

- @@ -198,16 +223,20 @@ export const ShowResources = ({ id, type }: Props) => {

Memory soft limit in bytes. Example: 256MB = - 268435456 bytes + 268435456 bytes. Use +/- buttons to adjust by 256 + MB.

- @@ -234,17 +263,20 @@ export const ShowResources = ({ id, type }: Props) => {

CPU quota in units of 10^-9 CPUs. Example: 2 - CPUs = 2000000000 + CPUs = 2000000000. Use +/- buttons to adjust by + 0.25 CPU.

- @@ -271,14 +303,21 @@ export const ShowResources = ({ id, type }: Props) => {

CPU shares (relative weight). Example: 1 CPU = - 1000000000 + 1000000000. Use +/- buttons to adjust by 0.25 + CPU.

- + diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 32f7910290..f8e6fab68a 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -123,7 +123,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { previewCertificateType: data.previewCertificateType || "none", previewCustomCertResolver: data.previewCustomCertResolver || "", previewRequireCollaboratorPermissions: - data.previewRequireCollaboratorPermissions || true, + data.previewRequireCollaboratorPermissions ?? true, }); } }, [data]); diff --git a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx index 1cb3d34af0..e85b1b0049 100644 --- a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx @@ -1,5 +1,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { + CheckIcon, + ChevronsUpDown, DatabaseZap, Info, PenBoxIcon, @@ -13,6 +15,14 @@ import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; import { Dialog, DialogContent, @@ -31,6 +41,12 @@ import { 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 { Select, SelectContent, @@ -48,6 +64,7 @@ import { import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import type { CacheType } from "../domains/handle-domain"; +import { getTimezoneLabel, TIMEZONES } from "./timezones"; export const commonCronExpressions = [ { label: "Every minute", value: "* * * * *" }, @@ -60,30 +77,6 @@ export const commonCronExpressions = [ { label: "Custom", value: "custom" }, ]; -export const commonTimezones = [ - { label: "UTC (Coordinated Universal Time)", value: "UTC" }, - { label: "America/New_York (Eastern Time)", value: "America/New_York" }, - { label: "America/Chicago (Central Time)", value: "America/Chicago" }, - { label: "America/Denver (Mountain Time)", value: "America/Denver" }, - { label: "America/Los_Angeles (Pacific Time)", value: "America/Los_Angeles" }, - { - label: "America/Mexico_City (Central Mexico)", - value: "America/Mexico_City", - }, - { label: "America/Sao_Paulo (Brasilia Time)", value: "America/Sao_Paulo" }, - { label: "Europe/London (Greenwich Mean Time)", value: "Europe/London" }, - { label: "Europe/Paris (Central European Time)", value: "Europe/Paris" }, - { label: "Europe/Berlin (Central European Time)", value: "Europe/Berlin" }, - { label: "Asia/Tokyo (Japan Standard Time)", value: "Asia/Tokyo" }, - { label: "Asia/Shanghai (China Standard Time)", value: "Asia/Shanghai" }, - { label: "Asia/Dubai (Gulf Standard Time)", value: "Asia/Dubai" }, - { label: "Asia/Kolkata (India Standard Time)", value: "Asia/Kolkata" }, - { - label: "Australia/Sydney (Australian Eastern Time)", - value: "Australia/Sydney", - }, -]; - const formSchema = z .object({ name: z.string().min(1, "Name is required"), @@ -512,25 +505,60 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { - + + + + + + + + + + + No timezone found. + + {Object.entries(TIMEZONES).map( + ([region, zones]) => ( + + {zones.map((tz) => ( + { + field.onChange(tz.value); + }} + > + {tz.value} + + + ))} + + ), + )} + + + + + Optional: Choose a timezone for the schedule execution time diff --git a/apps/dokploy/components/dashboard/application/schedules/timezones.ts b/apps/dokploy/components/dashboard/application/schedules/timezones.ts new file mode 100644 index 0000000000..44891b9097 --- /dev/null +++ b/apps/dokploy/components/dashboard/application/schedules/timezones.ts @@ -0,0 +1,458 @@ +// Complete list of IANA timezones grouped by region +export const TIMEZONES: Record< + string, + Array<{ label: string; value: string }> +> = { + Common: [{ label: "UTC (Coordinated Universal Time)", value: "UTC" }], + Africa: [ + { label: "Abidjan", value: "Africa/Abidjan" }, + { label: "Accra", value: "Africa/Accra" }, + { label: "Addis Ababa", value: "Africa/Addis_Ababa" }, + { label: "Algiers", value: "Africa/Algiers" }, + { label: "Asmara", value: "Africa/Asmara" }, + { label: "Bamako", value: "Africa/Bamako" }, + { label: "Bangui", value: "Africa/Bangui" }, + { label: "Banjul", value: "Africa/Banjul" }, + { label: "Bissau", value: "Africa/Bissau" }, + { label: "Blantyre", value: "Africa/Blantyre" }, + { label: "Brazzaville", value: "Africa/Brazzaville" }, + { label: "Bujumbura", value: "Africa/Bujumbura" }, + { label: "Cairo", value: "Africa/Cairo" }, + { label: "Casablanca", value: "Africa/Casablanca" }, + { label: "Ceuta", value: "Africa/Ceuta" }, + { label: "Conakry", value: "Africa/Conakry" }, + { label: "Dakar", value: "Africa/Dakar" }, + { label: "Dar es Salaam", value: "Africa/Dar_es_Salaam" }, + { label: "Djibouti", value: "Africa/Djibouti" }, + { label: "Douala", value: "Africa/Douala" }, + { label: "El Aaiun", value: "Africa/El_Aaiun" }, + { label: "Freetown", value: "Africa/Freetown" }, + { label: "Gaborone", value: "Africa/Gaborone" }, + { label: "Harare", value: "Africa/Harare" }, + { label: "Johannesburg", value: "Africa/Johannesburg" }, + { label: "Juba", value: "Africa/Juba" }, + { label: "Kampala", value: "Africa/Kampala" }, + { label: "Khartoum", value: "Africa/Khartoum" }, + { label: "Kigali", value: "Africa/Kigali" }, + { label: "Kinshasa", value: "Africa/Kinshasa" }, + { label: "Lagos", value: "Africa/Lagos" }, + { label: "Libreville", value: "Africa/Libreville" }, + { label: "Lome", value: "Africa/Lome" }, + { label: "Luanda", value: "Africa/Luanda" }, + { label: "Lubumbashi", value: "Africa/Lubumbashi" }, + { label: "Lusaka", value: "Africa/Lusaka" }, + { label: "Malabo", value: "Africa/Malabo" }, + { label: "Maputo", value: "Africa/Maputo" }, + { label: "Maseru", value: "Africa/Maseru" }, + { label: "Mbabane", value: "Africa/Mbabane" }, + { label: "Mogadishu", value: "Africa/Mogadishu" }, + { label: "Monrovia", value: "Africa/Monrovia" }, + { label: "Nairobi", value: "Africa/Nairobi" }, + { label: "Ndjamena", value: "Africa/Ndjamena" }, + { label: "Niamey", value: "Africa/Niamey" }, + { label: "Nouakchott", value: "Africa/Nouakchott" }, + { label: "Ouagadougou", value: "Africa/Ouagadougou" }, + { label: "Porto-Novo", value: "Africa/Porto-Novo" }, + { label: "Sao Tome", value: "Africa/Sao_Tome" }, + { label: "Tripoli", value: "Africa/Tripoli" }, + { label: "Tunis", value: "Africa/Tunis" }, + { label: "Windhoek", value: "Africa/Windhoek" }, + ], + America: [ + { label: "Adak", value: "America/Adak" }, + { label: "Anchorage", value: "America/Anchorage" }, + { label: "Anguilla", value: "America/Anguilla" }, + { label: "Antigua", value: "America/Antigua" }, + { label: "Araguaina", value: "America/Araguaina" }, + { + label: "Argentina/Buenos Aires", + value: "America/Argentina/Buenos_Aires", + }, + { label: "Argentina/Catamarca", value: "America/Argentina/Catamarca" }, + { label: "Argentina/Cordoba", value: "America/Argentina/Cordoba" }, + { label: "Argentina/Jujuy", value: "America/Argentina/Jujuy" }, + { label: "Argentina/La Rioja", value: "America/Argentina/La_Rioja" }, + { label: "Argentina/Mendoza", value: "America/Argentina/Mendoza" }, + { + label: "Argentina/Rio Gallegos", + value: "America/Argentina/Rio_Gallegos", + }, + { label: "Argentina/Salta", value: "America/Argentina/Salta" }, + { label: "Argentina/San Juan", value: "America/Argentina/San_Juan" }, + { label: "Argentina/San Luis", value: "America/Argentina/San_Luis" }, + { label: "Argentina/Tucuman", value: "America/Argentina/Tucuman" }, + { label: "Argentina/Ushuaia", value: "America/Argentina/Ushuaia" }, + { label: "Aruba", value: "America/Aruba" }, + { label: "Asuncion", value: "America/Asuncion" }, + { label: "Atikokan", value: "America/Atikokan" }, + { label: "Bahia", value: "America/Bahia" }, + { label: "Bahia Banderas", value: "America/Bahia_Banderas" }, + { label: "Barbados", value: "America/Barbados" }, + { label: "Belem", value: "America/Belem" }, + { label: "Belize", value: "America/Belize" }, + { label: "Blanc-Sablon", value: "America/Blanc-Sablon" }, + { label: "Boa Vista", value: "America/Boa_Vista" }, + { label: "Bogota", value: "America/Bogota" }, + { label: "Boise", value: "America/Boise" }, + { label: "Cambridge Bay", value: "America/Cambridge_Bay" }, + { label: "Campo Grande", value: "America/Campo_Grande" }, + { label: "Cancun", value: "America/Cancun" }, + { label: "Caracas", value: "America/Caracas" }, + { label: "Cayenne", value: "America/Cayenne" }, + { label: "Cayman", value: "America/Cayman" }, + { label: "Chicago (Central Time)", value: "America/Chicago" }, + { label: "Chihuahua", value: "America/Chihuahua" }, + { label: "Ciudad Juarez", value: "America/Ciudad_Juarez" }, + { label: "Costa Rica", value: "America/Costa_Rica" }, + { label: "Creston", value: "America/Creston" }, + { label: "Cuiaba", value: "America/Cuiaba" }, + { label: "Curacao", value: "America/Curacao" }, + { label: "Danmarkshavn", value: "America/Danmarkshavn" }, + { label: "Dawson", value: "America/Dawson" }, + { label: "Dawson Creek", value: "America/Dawson_Creek" }, + { label: "Denver (Mountain Time)", value: "America/Denver" }, + { label: "Detroit", value: "America/Detroit" }, + { label: "Dominica", value: "America/Dominica" }, + { label: "Edmonton", value: "America/Edmonton" }, + { label: "Eirunepe", value: "America/Eirunepe" }, + { label: "El Salvador", value: "America/El_Salvador" }, + { label: "Fort Nelson", value: "America/Fort_Nelson" }, + { label: "Fortaleza", value: "America/Fortaleza" }, + { label: "Glace Bay", value: "America/Glace_Bay" }, + { label: "Goose Bay", value: "America/Goose_Bay" }, + { label: "Grand Turk", value: "America/Grand_Turk" }, + { label: "Grenada", value: "America/Grenada" }, + { label: "Guadeloupe", value: "America/Guadeloupe" }, + { label: "Guatemala", value: "America/Guatemala" }, + { label: "Guayaquil", value: "America/Guayaquil" }, + { label: "Guyana", value: "America/Guyana" }, + { label: "Halifax", value: "America/Halifax" }, + { label: "Havana", value: "America/Havana" }, + { label: "Hermosillo", value: "America/Hermosillo" }, + { label: "Indiana/Indianapolis", value: "America/Indiana/Indianapolis" }, + { label: "Indiana/Knox", value: "America/Indiana/Knox" }, + { label: "Indiana/Marengo", value: "America/Indiana/Marengo" }, + { label: "Indiana/Petersburg", value: "America/Indiana/Petersburg" }, + { label: "Indiana/Tell City", value: "America/Indiana/Tell_City" }, + { label: "Indiana/Vevay", value: "America/Indiana/Vevay" }, + { label: "Indiana/Vincennes", value: "America/Indiana/Vincennes" }, + { label: "Indiana/Winamac", value: "America/Indiana/Winamac" }, + { label: "Inuvik", value: "America/Inuvik" }, + { label: "Iqaluit", value: "America/Iqaluit" }, + { label: "Jamaica", value: "America/Jamaica" }, + { label: "Juneau", value: "America/Juneau" }, + { label: "Kentucky/Louisville", value: "America/Kentucky/Louisville" }, + { label: "Kentucky/Monticello", value: "America/Kentucky/Monticello" }, + { label: "Kralendijk", value: "America/Kralendijk" }, + { label: "La Paz", value: "America/La_Paz" }, + { label: "Lima", value: "America/Lima" }, + { label: "Los Angeles (Pacific Time)", value: "America/Los_Angeles" }, + { label: "Lower Princes", value: "America/Lower_Princes" }, + { label: "Maceio", value: "America/Maceio" }, + { label: "Managua", value: "America/Managua" }, + { label: "Manaus", value: "America/Manaus" }, + { label: "Marigot", value: "America/Marigot" }, + { label: "Martinique", value: "America/Martinique" }, + { label: "Matamoros", value: "America/Matamoros" }, + { label: "Mazatlan", value: "America/Mazatlan" }, + { label: "Menominee", value: "America/Menominee" }, + { label: "Merida", value: "America/Merida" }, + { label: "Metlakatla", value: "America/Metlakatla" }, + { label: "Mexico City (Central Mexico)", value: "America/Mexico_City" }, + { label: "Miquelon", value: "America/Miquelon" }, + { label: "Moncton", value: "America/Moncton" }, + { label: "Monterrey", value: "America/Monterrey" }, + { label: "Montevideo", value: "America/Montevideo" }, + { label: "Montserrat", value: "America/Montserrat" }, + { label: "Nassau", value: "America/Nassau" }, + { label: "New York (Eastern Time)", value: "America/New_York" }, + { label: "Nome", value: "America/Nome" }, + { label: "Noronha", value: "America/Noronha" }, + { label: "North Dakota/Beulah", value: "America/North_Dakota/Beulah" }, + { label: "North Dakota/Center", value: "America/North_Dakota/Center" }, + { + label: "North Dakota/New Salem", + value: "America/North_Dakota/New_Salem", + }, + { label: "Nuuk", value: "America/Nuuk" }, + { label: "Ojinaga", value: "America/Ojinaga" }, + { label: "Panama", value: "America/Panama" }, + { label: "Paramaribo", value: "America/Paramaribo" }, + { label: "Phoenix", value: "America/Phoenix" }, + { label: "Port-au-Prince", value: "America/Port-au-Prince" }, + { label: "Port of Spain", value: "America/Port_of_Spain" }, + { label: "Porto Velho", value: "America/Porto_Velho" }, + { label: "Puerto Rico", value: "America/Puerto_Rico" }, + { label: "Punta Arenas", value: "America/Punta_Arenas" }, + { label: "Rankin Inlet", value: "America/Rankin_Inlet" }, + { label: "Recife", value: "America/Recife" }, + { label: "Regina", value: "America/Regina" }, + { label: "Resolute", value: "America/Resolute" }, + { label: "Rio Branco", value: "America/Rio_Branco" }, + { label: "Santarem", value: "America/Santarem" }, + { label: "Santiago", value: "America/Santiago" }, + { label: "Santo Domingo", value: "America/Santo_Domingo" }, + { label: "Sao Paulo (Brasilia Time)", value: "America/Sao_Paulo" }, + { label: "Scoresbysund", value: "America/Scoresbysund" }, + { label: "Sitka", value: "America/Sitka" }, + { label: "St Barthelemy", value: "America/St_Barthelemy" }, + { label: "St Johns", value: "America/St_Johns" }, + { label: "St Kitts", value: "America/St_Kitts" }, + { label: "St Lucia", value: "America/St_Lucia" }, + { label: "St Thomas", value: "America/St_Thomas" }, + { label: "St Vincent", value: "America/St_Vincent" }, + { label: "Swift Current", value: "America/Swift_Current" }, + { label: "Tegucigalpa", value: "America/Tegucigalpa" }, + { label: "Thule", value: "America/Thule" }, + { label: "Tijuana", value: "America/Tijuana" }, + { label: "Toronto", value: "America/Toronto" }, + { label: "Tortola", value: "America/Tortola" }, + { label: "Vancouver", value: "America/Vancouver" }, + { label: "Whitehorse", value: "America/Whitehorse" }, + { label: "Winnipeg", value: "America/Winnipeg" }, + { label: "Yakutat", value: "America/Yakutat" }, + ], + Antarctica: [ + { label: "Casey", value: "Antarctica/Casey" }, + { label: "Davis", value: "Antarctica/Davis" }, + { label: "DumontDUrville", value: "Antarctica/DumontDUrville" }, + { label: "Macquarie", value: "Antarctica/Macquarie" }, + { label: "Mawson", value: "Antarctica/Mawson" }, + { label: "McMurdo", value: "Antarctica/McMurdo" }, + { label: "Palmer", value: "Antarctica/Palmer" }, + { label: "Rothera", value: "Antarctica/Rothera" }, + { label: "Syowa", value: "Antarctica/Syowa" }, + { label: "Troll", value: "Antarctica/Troll" }, + { label: "Vostok", value: "Antarctica/Vostok" }, + ], + Arctic: [{ label: "Longyearbyen", value: "Arctic/Longyearbyen" }], + Asia: [ + { label: "Aden", value: "Asia/Aden" }, + { label: "Almaty", value: "Asia/Almaty" }, + { label: "Amman", value: "Asia/Amman" }, + { label: "Anadyr", value: "Asia/Anadyr" }, + { label: "Aqtau", value: "Asia/Aqtau" }, + { label: "Aqtobe", value: "Asia/Aqtobe" }, + { label: "Ashgabat", value: "Asia/Ashgabat" }, + { label: "Atyrau", value: "Asia/Atyrau" }, + { label: "Baghdad", value: "Asia/Baghdad" }, + { label: "Bahrain", value: "Asia/Bahrain" }, + { label: "Baku", value: "Asia/Baku" }, + { label: "Bangkok", value: "Asia/Bangkok" }, + { label: "Barnaul", value: "Asia/Barnaul" }, + { label: "Beirut", value: "Asia/Beirut" }, + { label: "Bishkek", value: "Asia/Bishkek" }, + { label: "Brunei", value: "Asia/Brunei" }, + { label: "Chita", value: "Asia/Chita" }, + { label: "Choibalsan", value: "Asia/Choibalsan" }, + { label: "Colombo", value: "Asia/Colombo" }, + { label: "Damascus", value: "Asia/Damascus" }, + { label: "Dhaka", value: "Asia/Dhaka" }, + { label: "Dili", value: "Asia/Dili" }, + { label: "Dubai (Gulf Standard Time)", value: "Asia/Dubai" }, + { label: "Dushanbe", value: "Asia/Dushanbe" }, + { label: "Famagusta", value: "Asia/Famagusta" }, + { label: "Gaza", value: "Asia/Gaza" }, + { label: "Hebron", value: "Asia/Hebron" }, + { label: "Ho Chi Minh", value: "Asia/Ho_Chi_Minh" }, + { label: "Hong Kong", value: "Asia/Hong_Kong" }, + { label: "Hovd", value: "Asia/Hovd" }, + { label: "Irkutsk", value: "Asia/Irkutsk" }, + { label: "Jakarta", value: "Asia/Jakarta" }, + { label: "Jayapura", value: "Asia/Jayapura" }, + { label: "Jerusalem", value: "Asia/Jerusalem" }, + { label: "Kabul", value: "Asia/Kabul" }, + { label: "Kamchatka", value: "Asia/Kamchatka" }, + { label: "Karachi", value: "Asia/Karachi" }, + { label: "Kathmandu", value: "Asia/Kathmandu" }, + { label: "Khandyga", value: "Asia/Khandyga" }, + { label: "Kolkata (India Standard Time)", value: "Asia/Kolkata" }, + { label: "Krasnoyarsk", value: "Asia/Krasnoyarsk" }, + { label: "Kuala Lumpur", value: "Asia/Kuala_Lumpur" }, + { label: "Kuching", value: "Asia/Kuching" }, + { label: "Kuwait", value: "Asia/Kuwait" }, + { label: "Macau", value: "Asia/Macau" }, + { label: "Magadan", value: "Asia/Magadan" }, + { label: "Makassar", value: "Asia/Makassar" }, + { label: "Manila", value: "Asia/Manila" }, + { label: "Muscat", value: "Asia/Muscat" }, + { label: "Nicosia", value: "Asia/Nicosia" }, + { label: "Novokuznetsk", value: "Asia/Novokuznetsk" }, + { label: "Novosibirsk", value: "Asia/Novosibirsk" }, + { label: "Omsk", value: "Asia/Omsk" }, + { label: "Oral", value: "Asia/Oral" }, + { label: "Phnom Penh", value: "Asia/Phnom_Penh" }, + { label: "Pontianak", value: "Asia/Pontianak" }, + { label: "Pyongyang", value: "Asia/Pyongyang" }, + { label: "Qatar", value: "Asia/Qatar" }, + { label: "Qostanay", value: "Asia/Qostanay" }, + { label: "Qyzylorda", value: "Asia/Qyzylorda" }, + { label: "Riyadh", value: "Asia/Riyadh" }, + { label: "Sakhalin", value: "Asia/Sakhalin" }, + { label: "Samarkand", value: "Asia/Samarkand" }, + { label: "Seoul", value: "Asia/Seoul" }, + { label: "Shanghai (China Standard Time)", value: "Asia/Shanghai" }, + { label: "Singapore", value: "Asia/Singapore" }, + { label: "Srednekolymsk", value: "Asia/Srednekolymsk" }, + { label: "Taipei", value: "Asia/Taipei" }, + { label: "Tashkent", value: "Asia/Tashkent" }, + { label: "Tbilisi", value: "Asia/Tbilisi" }, + { label: "Tehran", value: "Asia/Tehran" }, + { label: "Thimphu", value: "Asia/Thimphu" }, + { label: "Tokyo (Japan Standard Time)", value: "Asia/Tokyo" }, + { label: "Tomsk", value: "Asia/Tomsk" }, + { label: "Ulaanbaatar", value: "Asia/Ulaanbaatar" }, + { label: "Urumqi", value: "Asia/Urumqi" }, + { label: "Ust-Nera", value: "Asia/Ust-Nera" }, + { label: "Vientiane", value: "Asia/Vientiane" }, + { label: "Vladivostok", value: "Asia/Vladivostok" }, + { label: "Yakutsk", value: "Asia/Yakutsk" }, + { label: "Yangon", value: "Asia/Yangon" }, + { label: "Yekaterinburg", value: "Asia/Yekaterinburg" }, + { label: "Yerevan", value: "Asia/Yerevan" }, + ], + Atlantic: [ + { label: "Azores", value: "Atlantic/Azores" }, + { label: "Bermuda", value: "Atlantic/Bermuda" }, + { label: "Canary", value: "Atlantic/Canary" }, + { label: "Cape Verde", value: "Atlantic/Cape_Verde" }, + { label: "Faroe", value: "Atlantic/Faroe" }, + { label: "Madeira", value: "Atlantic/Madeira" }, + { label: "Reykjavik", value: "Atlantic/Reykjavik" }, + { label: "South Georgia", value: "Atlantic/South_Georgia" }, + { label: "St Helena", value: "Atlantic/St_Helena" }, + { label: "Stanley", value: "Atlantic/Stanley" }, + ], + Australia: [ + { label: "Adelaide", value: "Australia/Adelaide" }, + { label: "Brisbane", value: "Australia/Brisbane" }, + { label: "Broken Hill", value: "Australia/Broken_Hill" }, + { label: "Darwin", value: "Australia/Darwin" }, + { label: "Eucla", value: "Australia/Eucla" }, + { label: "Hobart", value: "Australia/Hobart" }, + { label: "Lindeman", value: "Australia/Lindeman" }, + { label: "Lord Howe", value: "Australia/Lord_Howe" }, + { label: "Melbourne", value: "Australia/Melbourne" }, + { label: "Perth", value: "Australia/Perth" }, + { label: "Sydney (Australian Eastern Time)", value: "Australia/Sydney" }, + ], + Europe: [ + { label: "Amsterdam", value: "Europe/Amsterdam" }, + { label: "Andorra", value: "Europe/Andorra" }, + { label: "Astrakhan", value: "Europe/Astrakhan" }, + { label: "Athens", value: "Europe/Athens" }, + { label: "Belgrade", value: "Europe/Belgrade" }, + { label: "Berlin (Central European Time)", value: "Europe/Berlin" }, + { label: "Bratislava", value: "Europe/Bratislava" }, + { label: "Brussels", value: "Europe/Brussels" }, + { label: "Bucharest", value: "Europe/Bucharest" }, + { label: "Budapest", value: "Europe/Budapest" }, + { label: "Busingen", value: "Europe/Busingen" }, + { label: "Chisinau", value: "Europe/Chisinau" }, + { label: "Copenhagen", value: "Europe/Copenhagen" }, + { label: "Dublin", value: "Europe/Dublin" }, + { label: "Gibraltar", value: "Europe/Gibraltar" }, + { label: "Guernsey", value: "Europe/Guernsey" }, + { label: "Helsinki", value: "Europe/Helsinki" }, + { label: "Isle of Man", value: "Europe/Isle_of_Man" }, + { label: "Istanbul", value: "Europe/Istanbul" }, + { label: "Jersey", value: "Europe/Jersey" }, + { label: "Kaliningrad", value: "Europe/Kaliningrad" }, + { label: "Kirov", value: "Europe/Kirov" }, + { label: "Kyiv", value: "Europe/Kyiv" }, + { label: "Lisbon", value: "Europe/Lisbon" }, + { label: "Ljubljana", value: "Europe/Ljubljana" }, + { label: "London (Greenwich Mean Time)", value: "Europe/London" }, + { label: "Luxembourg", value: "Europe/Luxembourg" }, + { label: "Madrid", value: "Europe/Madrid" }, + { label: "Malta", value: "Europe/Malta" }, + { label: "Mariehamn", value: "Europe/Mariehamn" }, + { label: "Minsk", value: "Europe/Minsk" }, + { label: "Monaco", value: "Europe/Monaco" }, + { label: "Moscow", value: "Europe/Moscow" }, + { label: "Oslo", value: "Europe/Oslo" }, + { label: "Paris (Central European Time)", value: "Europe/Paris" }, + { label: "Podgorica", value: "Europe/Podgorica" }, + { label: "Prague", value: "Europe/Prague" }, + { label: "Riga", value: "Europe/Riga" }, + { label: "Rome", value: "Europe/Rome" }, + { label: "Samara", value: "Europe/Samara" }, + { label: "San Marino", value: "Europe/San_Marino" }, + { label: "Sarajevo", value: "Europe/Sarajevo" }, + { label: "Saratov", value: "Europe/Saratov" }, + { label: "Simferopol", value: "Europe/Simferopol" }, + { label: "Skopje", value: "Europe/Skopje" }, + { label: "Sofia", value: "Europe/Sofia" }, + { label: "Stockholm", value: "Europe/Stockholm" }, + { label: "Tallinn", value: "Europe/Tallinn" }, + { label: "Tirane", value: "Europe/Tirane" }, + { label: "Ulyanovsk", value: "Europe/Ulyanovsk" }, + { label: "Vaduz", value: "Europe/Vaduz" }, + { label: "Vatican", value: "Europe/Vatican" }, + { label: "Vienna", value: "Europe/Vienna" }, + { label: "Vilnius", value: "Europe/Vilnius" }, + { label: "Volgograd", value: "Europe/Volgograd" }, + { label: "Warsaw", value: "Europe/Warsaw" }, + { label: "Zagreb", value: "Europe/Zagreb" }, + { label: "Zurich", value: "Europe/Zurich" }, + ], + Indian: [ + { label: "Antananarivo", value: "Indian/Antananarivo" }, + { label: "Chagos", value: "Indian/Chagos" }, + { label: "Christmas", value: "Indian/Christmas" }, + { label: "Cocos", value: "Indian/Cocos" }, + { label: "Comoro", value: "Indian/Comoro" }, + { label: "Kerguelen", value: "Indian/Kerguelen" }, + { label: "Mahe", value: "Indian/Mahe" }, + { label: "Maldives", value: "Indian/Maldives" }, + { label: "Mauritius", value: "Indian/Mauritius" }, + { label: "Mayotte", value: "Indian/Mayotte" }, + { label: "Reunion", value: "Indian/Reunion" }, + ], + Pacific: [ + { label: "Apia", value: "Pacific/Apia" }, + { label: "Auckland", value: "Pacific/Auckland" }, + { label: "Bougainville", value: "Pacific/Bougainville" }, + { label: "Chatham", value: "Pacific/Chatham" }, + { label: "Chuuk", value: "Pacific/Chuuk" }, + { label: "Easter", value: "Pacific/Easter" }, + { label: "Efate", value: "Pacific/Efate" }, + { label: "Fakaofo", value: "Pacific/Fakaofo" }, + { label: "Fiji", value: "Pacific/Fiji" }, + { label: "Funafuti", value: "Pacific/Funafuti" }, + { label: "Galapagos", value: "Pacific/Galapagos" }, + { label: "Gambier", value: "Pacific/Gambier" }, + { label: "Guadalcanal", value: "Pacific/Guadalcanal" }, + { label: "Guam", value: "Pacific/Guam" }, + { label: "Honolulu", value: "Pacific/Honolulu" }, + { label: "Kanton", value: "Pacific/Kanton" }, + { label: "Kiritimati", value: "Pacific/Kiritimati" }, + { label: "Kosrae", value: "Pacific/Kosrae" }, + { label: "Kwajalein", value: "Pacific/Kwajalein" }, + { label: "Majuro", value: "Pacific/Majuro" }, + { label: "Marquesas", value: "Pacific/Marquesas" }, + { label: "Midway", value: "Pacific/Midway" }, + { label: "Nauru", value: "Pacific/Nauru" }, + { label: "Niue", value: "Pacific/Niue" }, + { label: "Norfolk", value: "Pacific/Norfolk" }, + { label: "Noumea", value: "Pacific/Noumea" }, + { label: "Pago Pago", value: "Pacific/Pago_Pago" }, + { label: "Palau", value: "Pacific/Palau" }, + { label: "Pitcairn", value: "Pacific/Pitcairn" }, + { label: "Pohnpei", value: "Pacific/Pohnpei" }, + { label: "Port Moresby", value: "Pacific/Port_Moresby" }, + { label: "Rarotonga", value: "Pacific/Rarotonga" }, + { label: "Saipan", value: "Pacific/Saipan" }, + { label: "Tahiti", value: "Pacific/Tahiti" }, + { label: "Tarawa", value: "Pacific/Tarawa" }, + { label: "Tongatapu", value: "Pacific/Tongatapu" }, + { label: "Wake", value: "Pacific/Wake" }, + { label: "Wallis", value: "Pacific/Wallis" }, + ], +}; + +// Helper to get display label for a timezone value +export function getTimezoneLabel(value: string | undefined): string { + if (!value) return "UTC (default)"; + return value; +} diff --git a/apps/dokploy/components/dashboard/docker/logs/utils.ts b/apps/dokploy/components/dashboard/docker/logs/utils.ts index fc0b50928e..80a79eb2b6 100644 --- a/apps/dokploy/components/dashboard/docker/logs/utils.ts +++ b/apps/dokploy/components/dashboard/docker/logs/utils.ts @@ -108,7 +108,8 @@ export const getLogType = (message: string): LogStyle => { /(?:might|may|could)\s+(?:not|cause|lead\s+to)/i.test(lowerMessage) || /(?:!+\s*(?:warning|caution|attention)\s*!+)/i.test(lowerMessage) || /\b(?:deprecated|obsolete)\b/i.test(lowerMessage) || - /\b(?:unstable|experimental)\b/i.test(lowerMessage) + /\b(?:unstable|experimental)\b/i.test(lowerMessage) || + /⚠|⚠️/i.test(lowerMessage) ) { return LOG_STYLES.warning; } diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index 9ce42c1a09..a618a20aca 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -190,7 +190,9 @@ export const ShowProjects = () => { Create and manage your projects - {(auth?.role === "owner" || auth?.canCreateProjects) && ( + {(auth?.role === "owner" || + auth?.role === "admin" || + auth?.canCreateProjects) && (
diff --git a/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx b/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx index f751e262e8..979276995a 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx @@ -42,9 +42,7 @@ const AddRegistrySchema = z.object({ username: z.string().min(1, { message: "Username is required", }), - password: z.string().min(1, { - message: "Password is required", - }), + password: z.string(), registryUrl: z .string() .optional() @@ -75,6 +73,7 @@ const AddRegistrySchema = z.object({ ), imagePrefix: z.string(), serverId: z.string().optional(), + isEditing: z.boolean().optional(), }); type AddRegistry = z.infer; @@ -101,13 +100,21 @@ export const HandleRegistry = ({ registryId }: Props) => { const { mutateAsync, error, isError } = registryId ? api.registry.update.useMutation() : api.registry.create.useMutation(); - const { data: servers } = api.server.withSSHKey.useQuery(); + const { data: deployServers } = api.server.withSSHKey.useQuery(); + const { data: buildServers } = api.server.buildServers.useQuery(); + const servers = [...(deployServers || []), ...(buildServers || [])]; const { mutateAsync: testRegistry, isLoading, error: testRegistryError, isError: testRegistryIsError, } = api.registry.testRegistry.useMutation(); + const { + mutateAsync: testRegistryById, + isLoading: isLoadingById, + error: testRegistryByIdError, + isError: testRegistryByIdIsError, + } = api.registry.testRegistryById.useMutation(); const form = useForm({ defaultValues: { username: "", @@ -116,8 +123,26 @@ export const HandleRegistry = ({ registryId }: Props) => { imagePrefix: "", registryName: "", serverId: "", + isEditing: !!registryId, }, - resolver: zodResolver(AddRegistrySchema), + resolver: zodResolver( + AddRegistrySchema.refine( + (data) => { + // When creating a new registry, password is required + if ( + !data.isEditing && + (!data.password || data.password.length === 0) + ) { + return false; + } + return true; + }, + { + message: "Password is required", + path: ["password"], + }, + ), + ), }); const password = form.watch("password"); @@ -138,6 +163,7 @@ export const HandleRegistry = ({ registryId }: Props) => { registryUrl: registry.registryUrl, imagePrefix: registry.imagePrefix || "", registryName: registry.registryName, + isEditing: true, }); } else { form.reset({ @@ -146,13 +172,13 @@ export const HandleRegistry = ({ registryId }: Props) => { registryUrl: "", imagePrefix: "", serverId: "", + isEditing: false, }); } }, [form, form.reset, form.formState.isSubmitSuccessful, registry]); const onSubmit = async (data: AddRegistry) => { - await mutateAsync({ - password: data.password, + const payload: any = { registryName: data.registryName, username: data.username, registryUrl: data.registryUrl || "", @@ -160,7 +186,15 @@ export const HandleRegistry = ({ registryId }: Props) => { imagePrefix: data.imagePrefix, serverId: data.serverId, registryId: registryId || "", - }) + }; + + // Only include password if it's been provided (not empty) + // When editing, empty password means "keep the existing password" + if (data.password && data.password.length > 0) { + payload.password = data.password; + } + + await mutateAsync(payload) .then(async (_data) => { await utils.registry.all.invalidate(); toast.success(registryId ? "Registry updated" : "Registry added"); @@ -198,11 +232,14 @@ export const HandleRegistry = ({ registryId }: Props) => { Fill the next fields to add a external registry. - {(isError || testRegistryIsError) && ( + {(isError || testRegistryIsError || testRegistryByIdIsError) && (
- {testRegistryError?.message || error?.message || ""} + {testRegistryError?.message || + testRegistryByIdError?.message || + error?.message || + ""}
)} @@ -253,10 +290,20 @@ export const HandleRegistry = ({ registryId }: Props) => { name="password" render={({ field }) => ( - Password + Password{registryId && " (Optional)"} + {registryId && ( + + Leave blank to keep existing password. Enter new + password to test or update it. + + )} { + {deployServers && deployServers.length > 0 && ( + + Deploy Servers + {deployServers.map((server) => ( + + {server.name} + + ))} + + )} + {buildServers && buildServers.length > 0 && ( + + Build Servers + {buildServers.map((server) => ( + + {server.name} + + ))} + + )} - Servers - {servers?.map((server) => ( - - {server.name} - - ))} None @@ -387,8 +451,37 @@ export const HandleRegistry = ({ registryId }: Props) => { + + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > View Actions - + )}
Web server settings diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx index 4021ddaf5a..97cf3f6be9 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx @@ -7,9 +7,12 @@ interface Props { serverId?: string; } export const ToggleDockerCleanup = ({ serverId }: Props) => { - const { data, refetch } = api.user.get.useQuery(undefined, { - enabled: !serverId, - }); + const { data, refetch } = api.settings.getWebServerSettings.useQuery( + undefined, + { + enabled: !serverId, + }, + ); const { data: server, refetch: refetchServer } = api.server.one.useQuery( { @@ -22,7 +25,7 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => { const enabled = serverId ? server?.enableDockerCleanup - : data?.user.enableDockerCleanup; + : data?.enableDockerCleanup; const { mutateAsync } = api.settings.updateDockerCleanup.useMutation(); @@ -30,7 +33,10 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => { try { await mutateAsync({ enableDockerCleanup: checked, - serverId: serverId, + ...(serverId && { serverId }), + } as { + enableDockerCleanup: boolean; + serverId?: string; }); if (serverId) { await refetchServer(); diff --git a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx index b36aec7c43..99804ba6b1 100644 --- a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx @@ -1,5 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod"; -import { PlusIcon } from "lucide-react"; +import { Pencil, PlusIcon } from "lucide-react"; import Link from "next/link"; import { useTranslation } from "next-i18next"; import { useEffect, useState } from "react"; @@ -59,9 +59,10 @@ type Schema = z.infer; interface Props { serverId?: string; + asButton?: boolean; } -export const HandleServers = ({ serverId }: Props) => { +export const HandleServers = ({ serverId, asButton = false }: Props) => { const { t } = useTranslation("settings"); const utils = api.useUtils(); @@ -137,21 +138,32 @@ export const HandleServers = ({ serverId }: Props) => { return ( - - {serverId ? ( + {serverId ? ( + asButton ? ( + + + + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > Edit Server - ) : ( + ) + ) : ( + - )} - + + )} {serverId ? "Edit" : "Create"} Server diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx index fb7b23f786..09260b1a2a 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx @@ -80,7 +80,7 @@ const Schema = z.object({ type Schema = z.infer; export const SetupMonitoring = ({ serverId }: Props) => { - const { data } = serverId + const { data: serverData } = serverId ? api.server.one.useQuery( { serverId: serverId || "", @@ -89,7 +89,14 @@ export const SetupMonitoring = ({ serverId }: Props) => { enabled: !!serverId, }, ) - : api.user.getServerMetrics.useQuery(); + : { data: null }; + + const { data: webServerSettings } = + api.settings.getWebServerSettings.useQuery(undefined, { + enabled: !serverId, + }); + + const data = serverId ? serverData : webServerSettings; const url = useUrl(); diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx index 95899f20a1..d88e9a3e4f 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx @@ -1,5 +1,5 @@ import copy from "copy-to-clipboard"; -import { CopyIcon, ExternalLinkIcon, ServerIcon } from "lucide-react"; +import { CopyIcon, ExternalLinkIcon, ServerIcon, Settings } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { toast } from "sonner"; @@ -36,9 +36,10 @@ import { ValidateServer } from "./validate-server"; interface Props { serverId: string; + asButton?: boolean; } -export const SetupServer = ({ serverId }: Props) => { +export const SetupServer = ({ serverId, asButton = false }: Props) => { const [isOpen, setIsOpen] = useState(false); const { data: server } = api.server.one.useQuery( { @@ -81,14 +82,23 @@ export const SetupServer = ({ serverId }: Props) => { return ( - + {asButton ? ( + + + + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > Setup Server - + )}
diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index 2f8ac24e27..85e7f3ee7a 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -1,5 +1,18 @@ import { format } from "date-fns"; -import { KeyIcon, Loader2, MoreHorizontal, ServerIcon } from "lucide-react"; +import { + Clock, + Key, + KeyIcon, + Loader2, + MoreHorizontal, + Network, + Pencil, + ServerIcon, + Settings, + Terminal, + Trash2, + User, +} from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/router"; import { useTranslation } from "next-i18next"; @@ -24,14 +37,11 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { - Table, - TableBody, - TableCaption, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { ShowNodesModal } from "../cluster/nodes/show-nodes-modal"; import { TerminalModal } from "../web-server/terminal-modal"; @@ -59,7 +69,7 @@ export const ShowServers = () => { return (
{query?.success && isCloud && } - +
@@ -114,240 +124,309 @@ export const ShowServers = () => {
) : ( -
- - -
- See all servers -
-
- - - Name - {isCloud && ( - - Status - - )} - - Type - - - IP Address - - - Port - - - Username - - - SSH Key - - - Created - - - Actions - - - - - {data?.map((server) => { - const canDelete = server.totalSum === 0; - const isActive = server.serverStatus === "active"; - const isBuildServer = - server.serverType === "build"; - return ( - - - {server.name} - - {isCloud && ( - +
+
+ {data?.map((server) => { + const canDelete = server.totalSum === 0; + const isActive = server.serverStatus === "active"; + const isBuildServer = server.serverType === "build"; + return ( + + +
+
+ + + {server.name} + +
+ {isActive && + server.sshKeyId && + !isBuildServer && ( + + + + + + + Advanced + + + + {isCloud && ( + + )} + + + + + + )} +
+ +
+ {isCloud && ( + <> + {server.serverStatus === "active" ? ( + + {server.serverStatus} + + ) : ( + + + + + {server.serverStatus} + + + + +

+ This server is deactivated due + to lack of payment. Please pay + your invoice to reactivate it. + If you think this is an error, + please contact support. +

+
+
+ )} + + )} - {server.serverStatus} + {server.serverType} - - )} - - - {server.serverType} +
+
+
+ +
+ + + IP: + + + {server.ipAddress} - - - {server.ipAddress} - - - {server.port} - - - {server.username} - - - + + Port: + + + {server.port} + +
+
+ + + User: + + + {server.username} + +
+
+ + + SSH Key: + + {server.sshKeyId ? "Yes" : "No"} - - - +
+
+ + + Created{" "} {format( new Date(server.createdAt), - "PPpp", + "PPp", )} - - - - - - - - - - Actions - +
- {isActive && ( - <> - {server.sshKeyId && ( - - - {t( - "settings.common.enterTerminal", - )} - - - )} - - - - - {server.sshKeyId && - !isBuildServer && ( - + + {server.sshKeyId && ( + + +
+ - )} - - )} - - - You can not delete this server - because it has active services. - - You have active services - associated with this server, - please delete them first. - + asButton={true} + > + +
- ) - } - onClick={async () => { - await mutateAsync({ - serverId: server.serverId, - }) - .then(() => { - refetch(); - toast.success( - `Server ${server.name} deleted successfully`, - ); - }) - .catch((err) => { - toast.error(err.message); - }); - }} - > - e.preventDefault()} - > - Delete Server - - - - {isActive && - server.sshKeyId && - !isBuildServer && ( - <> - - - Extra - +
+ +

Terminal

+
+
+ )} - + +
+ - + + +

Setup Server

+
+ + + + +
+ - {isCloud && ( - + + +

Edit Server

+
+ + + {server.sshKeyId && !isBuildServer && ( + + +
+ - )} +
+
+ +

Web Server Actions

+
+
+ )} - - +
- - - )} - - - - - ); - })} - -
+ + +
+ + You can not delete this + server because it has + active services. + + You have active services + associated with this + server, please delete + them first. + +
+ ) + } + onClick={async () => { + await mutateAsync({ + serverId: server.serverId, + }) + .then(() => { + refetch(); + toast.success( + `Server ${server.name} deleted successfully`, + ); + }) + .catch((err) => { + toast.error(err.message); + }); + }} + > + + +
+ + +

+ {canDelete + ? "Delete Server" + : "Cannot delete - has active services"} +

+
+ + +
+ )} + + + ); + })} +
-
+
{data && data?.length > 0 && (
diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index c889708c3d..e0be5c7f3e 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -67,7 +67,7 @@ type AddServerDomain = z.infer; export const WebDomain = () => { const { t } = useTranslation("settings"); - const { data, refetch } = api.user.get.useQuery(); + const { data, refetch } = api.settings.getWebServerSettings.useQuery(); const { mutateAsync, isLoading } = api.settings.assignDomainServer.useMutation(); @@ -82,15 +82,15 @@ export const WebDomain = () => { }); const https = form.watch("https"); const domain = form.watch("domain") || ""; - const host = data?.user?.host || ""; + const host = data?.host || ""; const hasChanged = domain !== host; useEffect(() => { if (data) { form.reset({ - domain: data?.user?.host || "", - certificateType: data?.user?.certificateType, - letsEncryptEmail: data?.user?.letsEncryptEmail || "", - https: data?.user?.https || false, + domain: data?.host || "", + certificateType: data?.certificateType || "none", + letsEncryptEmail: data?.letsEncryptEmail || "", + https: data?.https || false, }); } }, [form, form.reset, data]); diff --git a/apps/dokploy/components/dashboard/settings/web-server.tsx b/apps/dokploy/components/dashboard/settings/web-server.tsx index 2a2ce4ab1b..c9cb7985b9 100644 --- a/apps/dokploy/components/dashboard/settings/web-server.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server.tsx @@ -16,7 +16,8 @@ import { UpdateServer } from "./web-server/update-server"; export const WebServer = () => { const { t } = useTranslation("settings"); - const { data } = api.user.get.useQuery(); + const { data: webServerSettings } = + api.settings.getWebServerSettings.useQuery(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); @@ -53,7 +54,7 @@ export const WebServer = () => {
- Server IP: {data?.user.serverIp} + Server IP: {webServerSettings?.serverIp} Version: {dokployVersion} diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx index 58e4c9d4eb..2647e1dc0e 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx @@ -24,10 +24,16 @@ const getTerminalKey = () => { interface Props { children?: React.ReactNode; serverId: string; + asButton?: boolean; } -export const TerminalModal = ({ children, serverId }: Props) => { +export const TerminalModal = ({ + children, + serverId, + asButton = false, +}: Props) => { const [terminalKey, setTerminalKey] = useState(getTerminalKey()); + const [isOpen, setIsOpen] = useState(false); const isLocalServer = serverId === "local"; const { data } = api.server.one.useQuery( @@ -43,15 +49,20 @@ export const TerminalModal = ({ children, serverId }: Props) => { }; return ( - - + + {asButton ? ( + {children} + ) : ( e.preventDefault()} + onSelect={(e) => { + e.preventDefault(); + setIsOpen(true); + }} > {children} - + )} event.preventDefault()} diff --git a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx index 3c49873de4..eb8c4a665e 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx @@ -46,15 +46,15 @@ interface Props { export const UpdateServerIp = ({ children }: Props) => { const [isOpen, setIsOpen] = useState(false); - const { data } = api.user.get.useQuery(); + const { data, refetch } = api.settings.getWebServerSettings.useQuery(); const { data: ip } = api.server.publicIp.useQuery(); const { mutateAsync, isLoading, error, isError } = - api.user.update.useMutation(); + api.settings.updateServerIp.useMutation(); const form = useForm({ defaultValues: { - serverIp: data?.user.serverIp || "", + serverIp: data?.serverIp || "", }, resolver: zodResolver(schema), }); @@ -62,13 +62,11 @@ export const UpdateServerIp = ({ children }: Props) => { useEffect(() => { if (data) { form.reset({ - serverIp: data.user.serverIp || "", + serverIp: data.serverIp || "", }); } }, [form, form.reset, data]); - const utils = api.useUtils(); - const setCurrentIp = () => { if (!ip) return; form.setValue("serverIp", ip); @@ -80,7 +78,7 @@ export const UpdateServerIp = ({ children }: Props) => { }) .then(async () => { toast.success("Server IP Updated"); - await utils.user.get.invalidate(); + await refetch(); setIsOpen(false); }) .catch(() => { diff --git a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx index 7bde4761e4..4b8dc91128 100644 --- a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx +++ b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx @@ -1,3 +1,4 @@ +import { ChevronDown } from "lucide-react"; import Link from "next/link"; import { Fragment } from "react"; import { @@ -5,18 +6,31 @@ import { BreadcrumbItem, BreadcrumbLink, BreadcrumbList, + BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Separator } from "@/components/ui/separator"; import { SidebarTrigger } from "@/components/ui/sidebar"; -interface Props { - list: { +interface BreadcrumbEntry { + name: string; + href?: string; + dropdownItems?: { name: string; - href?: string; + href: string; }[]; } +interface Props { + list: BreadcrumbEntry[]; +} + export const BreadcrumbSidebar = ({ list }: Props) => { return (
@@ -29,13 +43,29 @@ export const BreadcrumbSidebar = ({ list }: Props) => { {list.map((item, index) => ( - - {item.href ? ( - {item?.name} - ) : ( - item?.name - )} - + {item.dropdownItems && item.dropdownItems.length > 0 ? ( + + + {item.name} + + + + {item.dropdownItems.map((subItem) => ( + + {subItem.name} + + ))} + + + ) : ( + + {item.href ? ( + {item?.name} + ) : ( + {item?.name} + )} + + )} {index + 1 < list.length && ( diff --git a/apps/dokploy/components/ui/number-input.tsx b/apps/dokploy/components/ui/number-input.tsx new file mode 100644 index 0000000000..511b6dc9f8 --- /dev/null +++ b/apps/dokploy/components/ui/number-input.tsx @@ -0,0 +1,84 @@ +import { MinusIcon, PlusIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; + +export interface UnitConverter { + toValue: (raw: string | undefined) => number; + fromValue: (value: number) => string; + formatDisplay: (value: number) => string; +} + +export const createConverter = ( + multiplier: number, + formatDisplay: (value: number) => string, +): UnitConverter => ({ + toValue: (raw) => { + if (!raw) return 0; + const value = Number.parseInt(raw, 10); + return Number.isNaN(value) ? 0 : value / multiplier; + }, + fromValue: (value) => + value <= 0 ? "" : String(Math.round(value * multiplier)), + formatDisplay, +}); + +interface NumberInputWithStepsProps { + value: string | undefined; + onChange: (value: string) => void; + placeholder: string; + step: number; + converter: UnitConverter; +} + +export const NumberInputWithSteps = ({ + value, + onChange, + placeholder, + step, + converter, +}: NumberInputWithStepsProps) => { + const numericValue = converter.toValue(value); + const displayValue = converter.formatDisplay(numericValue); + + const handleIncrement = () => + onChange(converter.fromValue(numericValue + step)); + const handleDecrement = () => + onChange(converter.fromValue(Math.max(0, numericValue - step))); + + return ( +
+
+ + onChange(e.target.value)} + className="text-center" + /> + +
+ {displayValue && ( + + {displayValue} + + )} +
+ ); +}; diff --git a/apps/dokploy/drizzle/0133_striped_the_order.sql b/apps/dokploy/drizzle/0133_striped_the_order.sql new file mode 100644 index 0000000000..2c6085089e --- /dev/null +++ b/apps/dokploy/drizzle/0133_striped_the_order.sql @@ -0,0 +1,114 @@ +CREATE TABLE "webServerSettings" ( + "id" text PRIMARY KEY NOT NULL, + "serverIp" text, + "certificateType" "certificateType" DEFAULT 'none' NOT NULL, + "https" boolean DEFAULT false NOT NULL, + "host" text, + "letsEncryptEmail" text, + "sshPrivateKey" text, + "enableDockerCleanup" boolean DEFAULT true NOT NULL, + "logCleanupCron" text DEFAULT '0 0 * * *', + "metricsConfig" jsonb 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 NOT NULL, + "cleanupCacheApplications" boolean DEFAULT false NOT NULL, + "cleanupCacheOnPreviews" boolean DEFAULT false NOT NULL, + "cleanupCacheOnCompose" boolean DEFAULT false NOT NULL, + "created_at" timestamp DEFAULT now(), + "updated_at" timestamp DEFAULT now() NOT NULL +); + +-- Migrate data from user table to webServerSettings +-- Get the owner user's data and insert into webServerSettings +INSERT INTO "webServerSettings" ( + "id", + "serverIp", + "certificateType", + "https", + "host", + "letsEncryptEmail", + "sshPrivateKey", + "enableDockerCleanup", + "logCleanupCron", + "metricsConfig", + "cleanupCacheApplications", + "cleanupCacheOnPreviews", + "cleanupCacheOnCompose", + "created_at", + "updated_at" +) +SELECT + gen_random_uuid()::text as "id", + u."serverIp", + COALESCE(u."certificateType", 'none') as "certificateType", + COALESCE(u."https", false) as "https", + u."host", + u."letsEncryptEmail", + u."sshPrivateKey", + COALESCE(u."enableDockerCleanup", true) as "enableDockerCleanup", + COALESCE(u."logCleanupCron", '0 0 * * *') as "logCleanupCron", + COALESCE( + u."metricsConfig", + '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb + ) as "metricsConfig", + COALESCE(u."cleanupCacheApplications", false) as "cleanupCacheApplications", + COALESCE(u."cleanupCacheOnPreviews", false) as "cleanupCacheOnPreviews", + COALESCE(u."cleanupCacheOnCompose", false) as "cleanupCacheOnCompose", + NOW() as "created_at", + NOW() as "updated_at" +FROM "user" u +INNER JOIN "member" m ON u."id" = m."user_id" +WHERE m."role" = 'owner' +ORDER BY m."created_at" ASC +LIMIT 1; + +-- If no owner found, create a default entry +INSERT INTO "webServerSettings" ( + "id", + "serverIp", + "certificateType", + "https", + "host", + "letsEncryptEmail", + "sshPrivateKey", + "enableDockerCleanup", + "logCleanupCron", + "metricsConfig", + "cleanupCacheApplications", + "cleanupCacheOnPreviews", + "cleanupCacheOnCompose", + "created_at", + "updated_at" +) +SELECT + gen_random_uuid()::text as "id", + NULL as "serverIp", + 'none' as "certificateType", + false as "https", + NULL as "host", + NULL as "letsEncryptEmail", + NULL as "sshPrivateKey", + true as "enableDockerCleanup", + '0 0 * * *' as "logCleanupCron", + '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb as "metricsConfig", + false as "cleanupCacheApplications", + false as "cleanupCacheOnPreviews", + false as "cleanupCacheOnCompose", + NOW() as "created_at", + NOW() as "updated_at" +WHERE NOT EXISTS ( + SELECT 1 FROM "webServerSettings" +); + + +--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "serverIp";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "certificateType";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "https";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "host";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "letsEncryptEmail";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "sshPrivateKey";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "enableDockerCleanup";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "logCleanupCron";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "metricsConfig";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "cleanupCacheApplications";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "cleanupCacheOnPreviews";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "cleanupCacheOnCompose"; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0133_snapshot.json b/apps/dokploy/drizzle/meta/0133_snapshot.json new file mode 100644 index 0000000000..7fbcbbd863 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0133_snapshot.json @@ -0,0 +1,6968 @@ +{ + "id": "b5cddb89-e0bc-42fd-8994-6609f672ee0c", + "prevId": "d612e417-72a3-4619-829e-62daa3a5bf9c", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_user_id_user_id_fk": { + "name": "apikey_user_id_user_id_fk", + "tableFrom": "apikey", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteEnvironments": { + "name": "canDeleteEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateEnvironments": { + "name": "canCreateEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accessedEnvironments": { + "name": "accessedEnvironments", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_id_fk": { + "name": "organization_owner_id_user_id_fk", + "tableFrom": "organization", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_id_fk": { + "name": "two_factor_user_id_user_id_fk", + "tableFrom": "two_factor", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai": { + "name": "ai", + "schema": "", + "columns": { + "aiId": { + "name": "aiId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiUrl": { + "name": "apiUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isEnabled": { + "name": "isEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ai_organizationId_organization_id_fk": { + "name": "ai_organizationId_organization_id_fk", + "tableFrom": "ai", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildSecrets": { + "name": "previewBuildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLabels": { + "name": "previewLabels", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewCustomCertResolver": { + "name": "previewCustomCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewRequireCollaboratorPermissions": { + "name": "previewRequireCollaboratorPermissions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "rollbackActive": { + "name": "rollbackActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildSecrets": { + "name": "buildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "cleanCache": { + "name": "cleanCache", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBuildPath": { + "name": "giteaBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "railpackVersion": { + "name": "railpackVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0.2.2'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isStaticSpa": { + "name": "isStaticSpa", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "createEnvFile": { + "name": "createEnvFile", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackRegistryId": { + "name": "rollbackRegistryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildServerId": { + "name": "buildServerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildRegistryId": { + "name": "buildRegistryId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_rollbackRegistryId_registry_registryId_fk": { + "name": "application_rollbackRegistryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "rollbackRegistryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_environmentId_environment_environmentId_fk": { + "name": "application_environmentId_environment_environmentId_fk", + "tableFrom": "application", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_giteaId_gitea_giteaId_fk": { + "name": "application_giteaId_gitea_giteaId_fk", + "tableFrom": "application", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_buildServerId_server_serverId_fk": { + "name": "application_buildServerId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "buildServerId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_buildRegistryId_registry_registryId_fk": { + "name": "application_buildRegistryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "buildRegistryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "backupType": { + "name": "backupType", + "type": "backupType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'database'" + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_composeId_compose_composeId_fk": { + "name": "backup_composeId_compose_composeId_fk", + "tableFrom": "backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_userId_user_id_fk": { + "name": "backup_userId_user_id_fk", + "tableFrom": "backup", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "backup_appName_unique": { + "name": "backup_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketEmail": { + "name": "bitbucketEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "apiToken": { + "name": "apiToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeploymentsVolume": { + "name": "isolatedDeploymentsVolume", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_environmentId_environment_environmentId_fk": { + "name": "compose_environmentId_environment_environmentId_fk", + "tableFrom": "compose", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_giteaId_gitea_giteaId_fk": { + "name": "compose_giteaId_gitea_giteaId_fk", + "tableFrom": "compose", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pid": { + "name": "pid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startedAt": { + "name": "startedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finishedAt": { + "name": "finishedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildServerId": { + "name": "buildServerId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_scheduleId_schedule_scheduleId_fk": { + "name": "deployment_scheduleId_schedule_scheduleId_fk", + "tableFrom": "deployment", + "tableTo": "schedule", + "columnsFrom": [ + "scheduleId" + ], + "columnsTo": [ + "scheduleId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_backupId_backup_backupId_fk": { + "name": "deployment_backupId_backup_backupId_fk", + "tableFrom": "deployment", + "tableTo": "backup", + "columnsFrom": [ + "backupId" + ], + "columnsTo": [ + "backupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_rollbackId_rollback_rollbackId_fk": { + "name": "deployment_rollbackId_rollback_rollbackId_fk", + "tableFrom": "deployment", + "tableTo": "rollback", + "columnsFrom": [ + "rollbackId" + ], + "columnsTo": [ + "rollbackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_volumeBackupId_volume_backup_volumeBackupId_fk": { + "name": "deployment_volumeBackupId_volume_backup_volumeBackupId_fk", + "tableFrom": "deployment", + "tableTo": "volume_backup", + "columnsFrom": [ + "volumeBackupId" + ], + "columnsTo": [ + "volumeBackupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_buildServerId_server_serverId_fk": { + "name": "deployment_buildServerId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "buildServerId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customCertResolver": { + "name": "customCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "internalPath": { + "name": "internalPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "stripPath": { + "name": "stripPath", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isDefault": { + "name": "isDefault", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "environment_projectId_project_projectId_fk": { + "name": "environment_projectId_project_projectId_fk", + "tableFrom": "environment", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "git_provider_userId_user_id_fk": { + "name": "git_provider_userId_user_id_fk", + "tableFrom": "git_provider", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitea": { + "name": "gitea", + "schema": "", + "columns": { + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "giteaUrl": { + "name": "giteaUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitea.com'" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'repo,repo:status,read:user,read:org'" + }, + "last_authenticated_at": { + "name": "last_authenticated_at", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "gitea_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitea_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitea", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_environmentId_environment_environmentId_fk": { + "name": "mariadb_environmentId_environment_environmentId_fk", + "tableFrom": "mariadb", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_environmentId_environment_environmentId_fk": { + "name": "mongo_environmentId_environment_environmentId_fk", + "tableFrom": "mongo", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_environmentId_environment_environmentId_fk": { + "name": "mysql_environmentId_environment_environmentId_fk", + "tableFrom": "mysql", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom": { + "name": "custom", + "schema": "", + "columns": { + "customId": { + "name": "customId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lark": { + "name": "lark", + "schema": "", + "columns": { + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "volumeBackup": { + "name": "volumeBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customId": { + "name": "customId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_ntfyId_ntfy_ntfyId_fk": { + "name": "notification_ntfyId_ntfy_ntfyId_fk", + "tableFrom": "notification", + "tableTo": "ntfy", + "columnsFrom": [ + "ntfyId" + ], + "columnsTo": [ + "ntfyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_customId_custom_customId_fk": { + "name": "notification_customId_custom_customId_fk", + "tableFrom": "notification", + "tableTo": "custom", + "columnsFrom": [ + "customId" + ], + "columnsTo": [ + "customId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_larkId_lark_larkId_fk": { + "name": "notification_larkId_lark_larkId_fk", + "tableFrom": "notification", + "tableTo": "lark", + "columnsFrom": [ + "larkId" + ], + "columnsTo": [ + "larkId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ntfy": { + "name": "ntfy", + "schema": "", + "columns": { + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "accessToken": { + "name": "accessToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "messageThreadId": { + "name": "messageThreadId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publishMode": { + "name": "publishMode", + "type": "publishModeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'host'" + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_environmentId_environment_environmentId_fk": { + "name": "postgres_environmentId_environment_environmentId_fk", + "tableFrom": "postgres", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_environmentId_environment_environmentId_fk": { + "name": "redis_environmentId_environment_environmentId_fk", + "tableFrom": "redis", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rollback": { + "name": "rollback", + "schema": "", + "columns": { + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fullContext": { + "name": "fullContext", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rollback_deploymentId_deployment_deploymentId_fk": { + "name": "rollback_deploymentId_deployment_deploymentId_fk", + "tableFrom": "rollback", + "tableTo": "deployment", + "columnsFrom": [ + "deploymentId" + ], + "columnsTo": [ + "deploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.schedule": { + "name": "schedule", + "schema": "", + "columns": { + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shellType": { + "name": "shellType", + "type": "shellType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'bash'" + }, + "scheduleType": { + "name": "scheduleType", + "type": "scheduleType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "script": { + "name": "script", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_applicationId_application_applicationId_fk": { + "name": "schedule_applicationId_application_applicationId_fk", + "tableFrom": "schedule", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_composeId_compose_composeId_fk": { + "name": "schedule_composeId_compose_composeId_fk", + "tableFrom": "schedule", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_serverId_server_serverId_fk": { + "name": "schedule_serverId_server_serverId_fk", + "tableFrom": "schedule", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_userId_user_id_fk": { + "name": "schedule_userId_user_id_fk", + "tableFrom": "schedule", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "serverType": { + "name": "serverType", + "type": "serverType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'deploy'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_id_fk": { + "name": "session_temp_user_id_user_id_fk", + "tableFrom": "session_temp", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "firstName": { + "name": "firstName", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "lastName": { + "name": "lastName", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allowImpersonation": { + "name": "allowImpersonation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.volume_backup": { + "name": "volume_backup", + "schema": "", + "columns": { + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "turnOff": { + "name": "turnOff", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "volume_backup_applicationId_application_applicationId_fk": { + "name": "volume_backup_applicationId_application_applicationId_fk", + "tableFrom": "volume_backup", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_postgresId_postgres_postgresId_fk": { + "name": "volume_backup_postgresId_postgres_postgresId_fk", + "tableFrom": "volume_backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mariadbId_mariadb_mariadbId_fk": { + "name": "volume_backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "volume_backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mongoId_mongo_mongoId_fk": { + "name": "volume_backup_mongoId_mongo_mongoId_fk", + "tableFrom": "volume_backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mysqlId_mysql_mysqlId_fk": { + "name": "volume_backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "volume_backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_redisId_redis_redisId_fk": { + "name": "volume_backup_redisId_redis_redisId_fk", + "tableFrom": "volume_backup", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_composeId_compose_composeId_fk": { + "name": "volume_backup_composeId_compose_composeId_fk", + "tableFrom": "volume_backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_destinationId_destination_destinationId_fk": { + "name": "volume_backup_destinationId_destination_destinationId_fk", + "tableFrom": "volume_backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webServerSettings": { + "name": "webServerSettings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "logCleanupCron": { + "name": "logCleanupCron", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0 0 * * *'" + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + "railpack" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "drop" + ] + }, + "public.backupType": { + "name": "backupType", + "schema": "public", + "values": [ + "database", + "compose" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo", + "web-server" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "raw" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error", + "cancelled" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket", + "gitea" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify", + "ntfy", + "custom", + "lark" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.publishModeType": { + "name": "publishModeType", + "schema": "public", + "values": [ + "ingress", + "host" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.scheduleType": { + "name": "scheduleType", + "schema": "public", + "values": [ + "application", + "compose", + "server", + "dokploy-server" + ] + }, + "public.shellType": { + "name": "shellType", + "schema": "public", + "values": [ + "bash", + "sh" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + }, + "public.serverType": { + "name": "serverType", + "schema": "public", + "values": [ + "deploy", + "build" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none", + "custom" + ] + }, + "public.triggerType": { + "name": "triggerType", + "schema": "public", + "values": [ + "push", + "tag" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index cdd5cbef3c..bd6c62c46c 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -932,6 +932,13 @@ "when": 1765346573500, "tag": "0132_clean_layla_miller", "breakpoints": true + }, + { + "idx": 133, + "version": "7", + "when": 1766301478005, + "tag": "0133_striped_the_order", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 51785dd5c6..c8bb95056e 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.26.2", + "version": "v0.26.3", "private": true, "license": "Apache-2.0", "type": "module", @@ -140,7 +140,6 @@ "react-i18next": "^15.5.2", "react-markdown": "^9.1.0", "recharts": "^2.15.3", - "rotating-file-stream": "3.2.3", "slugify": "^1.6.6", "sonner": "^1.7.4", "ssh2": "1.15.0", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx index a2e54ad51c..dcc34cec22 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx @@ -279,6 +279,16 @@ const EnvironmentPage = ( const [isBulkActionLoading, setIsBulkActionLoading] = useState(false); const { projectId, environmentId } = props; const { data: auth } = api.user.get.useQuery(); + + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: projectId, + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; + const [sortBy, setSortBy] = useState(() => { if (typeof window !== "undefined") { return localStorage.getItem("servicesSort") || "lastDeploy-desc"; @@ -863,6 +873,7 @@ const EnvironmentPage = ( }, { name: currentEnvironment.name, + dropdownItems: environmentDropdownItems, }, ]} /> diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx index a20d307b3f..e6fc9ddf04 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx @@ -91,6 +91,15 @@ const Service = ( const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: auth } = api.user.get.useQuery(); + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: data?.environment?.project?.projectId || "", + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; + return (
@@ -98,11 +107,11 @@ const Service = ( list={[ { name: "Projects", href: "/dashboard/projects" }, { - name: data?.environment.project.name || "", + name: data?.environment?.project?.name || "", }, { name: data?.environment?.name || "", - href: `/dashboard/project/${projectId}/environment/${environmentId}`, + dropdownItems: environmentDropdownItems, }, { name: data?.name || "", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx index df7cb9a9c4..56b4b5d0c2 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx @@ -80,6 +80,14 @@ const Service = ( const { data: auth } = api.user.get.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: data?.environment?.projectId || "", + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; return (
@@ -92,7 +100,7 @@ const Service = ( }, { name: data?.environment?.name || "", - href: `/dashboard/project/${projectId}/environment/${environmentId}`, + dropdownItems: environmentDropdownItems, }, { name: data?.name || "", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx index e5133a9bbc..d47fbd14d8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx @@ -62,6 +62,15 @@ const Mariadb = ( const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: data?.environment?.projectId || "", + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; + return (
@@ -73,7 +82,7 @@ const Mariadb = ( }, { name: data?.environment?.name || "", - href: `/dashboard/project/${projectId}/environment/${environmentId}`, + dropdownItems: environmentDropdownItems, }, { name: data?.name || "", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx index 2de7350b78..660315d5a5 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx @@ -61,6 +61,14 @@ const Mongo = ( const { data: auth } = api.user.get.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: data?.environment?.projectId || "", + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; return (
@@ -73,7 +81,7 @@ const Mongo = ( }, { name: data?.environment?.name || "", - href: `/dashboard/project/${projectId}/environment/${environmentId}`, + dropdownItems: environmentDropdownItems, }, { name: data?.name || "", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx index 23227f3854..7f4cc791c5 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx @@ -60,6 +60,14 @@ const MySql = ( const { data: auth } = api.user.get.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: data?.environment?.projectId || "", + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; return (
@@ -72,7 +80,7 @@ const MySql = ( }, { name: data?.environment?.name || "", - href: `/dashboard/project/${projectId}/environment/${environmentId}`, + dropdownItems: environmentDropdownItems, }, { name: data?.name || "", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx index 8fe7742e3a..a34f7b7ee0 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx @@ -60,6 +60,14 @@ const Postgresql = ( const { data: auth } = api.user.get.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: data?.environment?.projectId || "", + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; return (
@@ -72,7 +80,7 @@ const Postgresql = ( }, { name: data?.environment?.name || "", - href: `/dashboard/project/${projectId}/environment/${environmentId}`, + dropdownItems: environmentDropdownItems, }, { name: data?.name || "", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx index 14c8730945..72a513fba2 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx @@ -60,6 +60,14 @@ const Redis = ( const { data: auth } = api.user.get.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: environments } = api.environment.byProjectId.useQuery({ + projectId: data?.environment?.projectId || "", + }); + const environmentDropdownItems = + environments?.map((env) => ({ + name: env.name, + href: `/dashboard/project/${projectId}/environment/${env.environmentId}`, + })) || []; return (
@@ -72,7 +80,7 @@ const Redis = ( }, { name: data?.environment?.name || "", - href: `/dashboard/project/${projectId}/environment/${environmentId}`, + dropdownItems: environmentDropdownItems, }, { name: data?.name || "", diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 6a3be31b45..4323d9e477 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -1,8 +1,8 @@ import { - findUserById, + getWebServerSettings, IS_CLOUD, setupWebMonitoring, - updateUser, + updateWebServerSettings, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { apiUpdateWebServerMonitoring } from "@/server/db/schema"; @@ -11,7 +11,7 @@ import { adminProcedure, createTRPCRouter } from "../trpc"; export const adminRouter = createTRPCRouter({ setupMonitoring: adminProcedure .input(apiUpdateWebServerMonitoring) - .mutation(async ({ input, ctx }) => { + .mutation(async ({ input }) => { try { if (IS_CLOUD) { throw new TRPCError({ @@ -19,15 +19,8 @@ export const adminRouter = createTRPCRouter({ message: "Feature disabled on cloud", }); } - const user = await findUserById(ctx.user.ownerId); - if (user.id !== ctx.user.ownerId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to setup the monitoring", - }); - } - await updateUser(user.id, { + await updateWebServerSettings({ metricsConfig: { server: { type: "Dokploy", @@ -52,8 +45,9 @@ export const adminRouter = createTRPCRouter({ }, }); - const currentServer = await setupWebMonitoring(user.id); - return currentServer; + await setupWebMonitoring(); + const settings = await getWebServerSettings(); + return settings; } catch (error) { throw error; } diff --git a/apps/dokploy/server/api/routers/ai.ts b/apps/dokploy/server/api/routers/ai.ts index e03da905d3..ff2d1ee8a8 100644 --- a/apps/dokploy/server/api/routers/ai.ts +++ b/apps/dokploy/server/api/routers/ai.ts @@ -68,6 +68,40 @@ export const aiRouter = createTRPCRouter({ { headers: {} }, ); break; + case "perplexity": + // Perplexity doesn't have a /models endpoint, return hardcoded list + return [ + { + id: "sonar-deep-research", + object: "model", + created: Date.now(), + owned_by: "perplexity", + }, + { + id: "sonar-reasoning-pro", + object: "model", + created: Date.now(), + owned_by: "perplexity", + }, + { + id: "sonar-reasoning", + object: "model", + created: Date.now(), + owned_by: "perplexity", + }, + { + id: "sonar-pro", + object: "model", + created: Date.now(), + owned_by: "perplexity", + }, + { + id: "sonar", + object: "model", + created: Date.now(), + owned_by: "perplexity", + }, + ] as Model[]; default: if (!input.apiKey) throw new TRPCError({ diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 3261f61fa8..4ad2ca00a0 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -17,8 +17,8 @@ import { findGitProviderById, findProjectById, findServerById, - findUserById, getComposeContainer, + getWebServerSettings, IS_CLOUD, loadServices, randomizeComposeFile, @@ -569,8 +569,7 @@ export const composeRouter = createTRPCRouter({ const template = await fetchTemplateFiles(input.id, input.baseUrl); - const admin = await findUserById(ctx.user.ownerId); - let serverIp = admin.serverIp || "127.0.0.1"; + let serverIp = "127.0.0.1"; const project = await findProjectById(environment.projectId); @@ -579,6 +578,9 @@ export const composeRouter = createTRPCRouter({ serverIp = server.ipAddress; } else if (process.env.NODE_ENV === "development") { serverIp = "127.0.0.1"; + } else { + const settings = await getWebServerSettings(); + serverIp = settings?.serverIp || "127.0.0.1"; } const projectName = slugify(`${project.name} ${input.id}`); @@ -803,14 +805,16 @@ export const composeRouter = createTRPCRouter({ const decodedData = Buffer.from(input.base64, "base64").toString( "utf-8", ); - const admin = await findUserById(ctx.user.ownerId); - let serverIp = admin.serverIp || "127.0.0.1"; + let serverIp = "127.0.0.1"; if (compose.serverId) { const server = await findServerById(compose.serverId); serverIp = server.ipAddress; } else if (process.env.NODE_ENV === "development") { serverIp = "127.0.0.1"; + } else { + const settings = await getWebServerSettings(); + serverIp = settings?.serverIp || "127.0.0.1"; } const templateData = JSON.parse(decodedData); const config = parse(templateData.config) as CompleteTemplate; @@ -880,14 +884,16 @@ export const composeRouter = createTRPCRouter({ await removeDomainById(domain.domainId); } - const admin = await findUserById(ctx.user.ownerId); - let serverIp = admin.serverIp || "127.0.0.1"; + let serverIp = "127.0.0.1"; if (compose.serverId) { const server = await findServerById(compose.serverId); serverIp = server.ipAddress; } else if (process.env.NODE_ENV === "development") { serverIp = "127.0.0.1"; + } else { + const settings = await getWebServerSettings(); + serverIp = settings?.serverIp || "127.0.0.1"; } const templateData = JSON.parse(decodedData); diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts index 1f6264351f..5767a38a9a 100644 --- a/apps/dokploy/server/api/routers/domain.ts +++ b/apps/dokploy/server/api/routers/domain.ts @@ -9,6 +9,7 @@ import { findPreviewDeploymentById, findServerById, generateTraefikMeDomain, + getWebServerSettings, manageDomain, removeDomain, removeDomainById, @@ -107,16 +108,13 @@ export const domainRouter = createTRPCRouter({ }), canGenerateTraefikMeDomains: protectedProcedure .input(z.object({ serverId: z.string() })) - .query(async ({ input, ctx }) => { - const organization = await findOrganizationById( - ctx.session.activeOrganizationId, - ); - + .query(async ({ input }) => { if (input.serverId) { const server = await findServerById(input.serverId); return server.ipAddress; } - return organization?.owner.serverIp; + const settings = await getWebServerSettings(); + return settings?.serverIp || ""; }), update: protectedProcedure diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 18950b7a3f..7d4bd2e506 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -87,7 +87,7 @@ export const mariadbRouter = createTRPCRouter({ type: "volume", }); - return true; + return newMariadb; } catch (error) { if (error instanceof TRPCError) { throw error; diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index 51b830fc86..ae0fa47411 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -87,7 +87,7 @@ export const mongoRouter = createTRPCRouter({ type: "volume", }); - return true; + return newMongo; } catch (error) { if (error instanceof TRPCError) { throw error; diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 5edb27da4a..5204fedc8c 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -89,7 +89,7 @@ export const mysqlRouter = createTRPCRouter({ type: "volume", }); - return true; + return newMysql; } catch (error) { if (error instanceof TRPCError) { throw error; diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index b32278465e..303168b9f5 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -8,6 +8,7 @@ import { createSlackNotification, createTelegramNotification, findNotificationById, + getWebServerSettings, IS_CLOUD, removeNotificationById, sendCustomNotification, @@ -66,7 +67,6 @@ import { apiUpdateTelegram, notifications, server, - user, } from "@/server/db/schema"; export const notificationRouter = createTRPCRouter({ @@ -364,21 +364,20 @@ export const notificationRouter = createTRPCRouter({ let organizationId = ""; let ServerName = ""; if (input.ServerType === "Dokploy") { - const result = await db - .select() - .from(user) - .where( - sql`${user.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`, - ); - - if (!result?.[0]?.id) { + const settings = await getWebServerSettings(); + if ( + !settings?.metricsConfig?.server?.token || + settings.metricsConfig.server.token !== input.Token + ) { throw new TRPCError({ code: "BAD_REQUEST", message: "Token not found", }); } - organizationId = result?.[0]?.id; + // For Dokploy server type, we don't have a specific organizationId + // This might need to be adjusted based on your business logic + organizationId = ""; ServerName = "Dokploy"; } else { const result = await db diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index 3112beb66a..e1718bff12 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -91,7 +91,7 @@ export const postgresRouter = createTRPCRouter({ type: "volume", }); - return true; + return newPostgres; } catch (error) { if (error instanceof TRPCError) { throw error; diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 082fbafff6..14155edfc3 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -15,6 +15,7 @@ import { apiFindOneRegistry, apiRemoveRegistry, apiTestRegistry, + apiTestRegistryById, apiUpdateRegistry, registry, } from "@/server/db/schema"; @@ -109,6 +110,67 @@ export const registryRouter = createTRPCRouter({ }); } + return true; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + error instanceof Error + ? error.message + : "Error testing the registry", + cause: error, + }); + } + }), + testRegistryById: protectedProcedure + .input(apiTestRegistryById) + .mutation(async ({ input, ctx }) => { + try { + // Get the full registry with password from database + const registryData = await db.query.registry.findFirst({ + where: eq(registry.registryId, input.registryId ?? ""), + }); + + if (!registryData) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Registry not found", + }); + } + + if (registryData.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not allowed to test this registry", + }); + } + + const args = [ + "login", + registryData.registryUrl, + "--username", + registryData.username, + "--password-stdin", + ]; + + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Select a server to test the registry", + }); + } + + if (input.serverId && input.serverId !== "none") { + await execAsyncRemote( + input.serverId, + `echo ${registryData.password} | docker ${args.join(" ")}`, + ); + } else { + await execFileAsync("docker", args, { + input: Buffer.from(registryData.password).toString(), + }); + } + return true; } catch (error) { throw new TRPCError({ diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index a6154ec1ce..bd182527a3 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -12,11 +12,11 @@ import { DEFAULT_UPDATE_DATA, execAsync, findServerById, - findUserById, getDokployImage, getDokployImageTag, getLogCleanupStatus, getUpdateData, + getWebServerSettings, IS_CLOUD, parseRawConfig, paths, @@ -40,7 +40,7 @@ import { updateLetsEncryptEmail, updateServerById, updateServerTraefik, - updateUser, + updateWebServerSettings, writeConfig, writeMainConfig, writeTraefikConfigInPath, @@ -77,6 +77,13 @@ import { } from "../trpc"; export const settingsRouter = createTRPCRouter({ + getWebServerSettings: protectedProcedure.query(async () => { + if (IS_CLOUD) { + return null; + } + const settings = await getWebServerSettings(); + return settings; + }), reloadServer: adminProcedure.mutation(async () => { if (IS_CLOUD) { return true; @@ -209,11 +216,11 @@ export const settingsRouter = createTRPCRouter({ }), saveSSHPrivateKey: adminProcedure .input(apiSaveSSHKey) - .mutation(async ({ input, ctx }) => { + .mutation(async ({ input }) => { if (IS_CLOUD) { return true; } - await updateUser(ctx.user.ownerId, { + await updateWebServerSettings({ sshPrivateKey: input.sshPrivateKey, }); @@ -221,36 +228,36 @@ export const settingsRouter = createTRPCRouter({ }), assignDomainServer: adminProcedure .input(apiAssignDomain) - .mutation(async ({ ctx, input }) => { + .mutation(async ({ input }) => { if (IS_CLOUD) { return true; } - const user = await updateUser(ctx.user.ownerId, { + const settings = await updateWebServerSettings({ host: input.host, letsEncryptEmail: input.letsEncryptEmail, certificateType: input.certificateType, https: input.https, }); - if (!user) { + if (!settings) { throw new TRPCError({ code: "NOT_FOUND", - message: "User not found", + message: "Web server settings not found", }); } - updateServerTraefik(user, input.host); + updateServerTraefik(settings, input.host); if (input.letsEncryptEmail) { updateLetsEncryptEmail(input.letsEncryptEmail); } - return user; + return settings; }), - cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { + cleanSSHPrivateKey: adminProcedure.mutation(async () => { if (IS_CLOUD) { return true; } - await updateUser(ctx.user.ownerId, { + await updateWebServerSettings({ sshPrivateKey: null, }); return true; @@ -310,11 +317,11 @@ export const settingsRouter = createTRPCRouter({ } } } else if (!IS_CLOUD) { - const userUpdated = await updateUser(ctx.user.ownerId, { + const settingsUpdated = await updateWebServerSettings({ enableDockerCleanup: input.enableDockerCleanup, }); - if (userUpdated?.enableDockerCleanup) { + if (settingsUpdated?.enableDockerCleanup) { scheduleJob("docker-cleanup", "0 0 * * *", async () => { console.log( `Docker Cleanup ${new Date().toLocaleString()}] Running...`, @@ -488,13 +495,28 @@ export const settingsRouter = createTRPCRouter({ return readConfigInPath(input.path, input.serverId); }), - getIp: protectedProcedure.query(async ({ ctx }) => { + getIp: protectedProcedure.query(async () => { if (IS_CLOUD) { - return true; + return ""; } - const user = await findUserById(ctx.user.ownerId); - return user.serverIp; + const settings = await getWebServerSettings(); + return settings?.serverIp || ""; }), + updateServerIp: adminProcedure + .input( + z.object({ + serverIp: z.string(), + }), + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + const settings = await updateWebServerSettings({ + serverIp: input.serverIp, + }); + return settings; + }), getOpenApiDocument: protectedProcedure.query( async ({ ctx }): Promise => { diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index a6bd81a012..e801a5adb2 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -5,6 +5,7 @@ import { findUserById, getDokployUrl, getUserByToken, + getWebServerSettings, IS_CLOUD, removeUserById, sendEmailNotification, @@ -214,10 +215,11 @@ export const userRouter = createTRPCRouter({ }), getMetricsToken: protectedProcedure.query(async ({ ctx }) => { const user = await findUserById(ctx.user.ownerId); + const settings = await getWebServerSettings(); return { - serverIp: user.serverIp, + serverIp: settings?.serverIp, enabledFeatures: user.enablePaidFeatures, - metricsConfig: user?.metricsConfig, + metricsConfig: settings?.metricsConfig, }; }), remove: protectedProcedure diff --git a/packages/server/package.json b/packages/server/package.json index e23fa6d8bd..9ce60fcf7e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -75,7 +75,6 @@ "qrcode": "^1.5.4", "react": "18.2.0", "react-dom": "18.2.0", - "rotating-file-stream": "3.2.3", "shell-quote": "^1.8.1", "slugify": "^1.6.6", "ssh2": "1.15.0", diff --git a/packages/server/src/db/schema/index.ts b/packages/server/src/db/schema/index.ts index c16ef1452f..ee3c03e933 100644 --- a/packages/server/src/db/schema/index.ts +++ b/packages/server/src/db/schema/index.ts @@ -35,3 +35,4 @@ export * from "./ssh-key"; export * from "./user"; export * from "./utils"; export * from "./volume-backups"; +export * from "./web-server-settings"; diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 425ce768cc..dd00bf19d8 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -80,6 +80,14 @@ export const apiTestRegistry = createSchema.pick({}).extend({ serverId: z.string().optional(), }); +export const apiTestRegistryById = createSchema + .pick({ + registryId: true, + }) + .extend({ + serverId: z.string().optional(), + }); + export const apiRemoveRegistry = createSchema .pick({ registryId: true, diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 5a96aa3eba..51be7a7eab 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -3,7 +3,6 @@ import { relations } from "drizzle-orm"; import { boolean, integer, - jsonb, pgTable, text, timestamp, @@ -15,7 +14,6 @@ import { account, apikey, organization } from "./account"; import { backups } from "./backups"; import { projects } from "./project"; import { schedules } from "./schedule"; -import { certificateType } from "./shared"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -51,73 +49,10 @@ export const user = pgTable("user", { banExpires: timestamp("ban_expires"), updatedAt: timestamp("updated_at").notNull(), // Admin - serverIp: text("serverIp"), - certificateType: certificateType("certificateType").notNull().default("none"), - https: boolean("https").notNull().default(false), - host: text("host"), - letsEncryptEmail: text("letsEncryptEmail"), - sshPrivateKey: text("sshPrivateKey"), - enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(true), - logCleanupCron: text("logCleanupCron").default("0 0 * * *"), role: text("role").notNull().default("user"), // Metrics enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), allowImpersonation: boolean("allowImpersonation").notNull().default(false), - metricsConfig: jsonb("metricsConfig") - .$type<{ - server: { - type: "Dokploy" | "Remote"; - refreshRate: number; - port: number; - token: string; - urlCallback: string; - retentionDays: number; - cronJob: string; - thresholds: { - cpu: number; - memory: number; - }; - }; - containers: { - refreshRate: number; - services: { - include: string[]; - exclude: string[]; - }; - }; - }>() - .notNull() - .default({ - server: { - type: "Dokploy", - refreshRate: 60, - port: 4500, - token: "", - retentionDays: 2, - cronJob: "", - urlCallback: "", - thresholds: { - cpu: 0, - memory: 0, - }, - }, - containers: { - refreshRate: 60, - services: { - include: [], - exclude: [], - }, - }, - }), - cleanupCacheApplications: boolean("cleanupCacheApplications") - .notNull() - .default(false), - cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") - .notNull() - .default(false), - cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") - .notNull() - .default(false), stripeCustomerId: text("stripeCustomerId"), stripeSubscriptionId: text("stripeSubscriptionId"), serversQuantity: integer("serversQuantity").notNull().default(0), @@ -203,33 +138,6 @@ export const apiFindOneUserByAuth = createSchema // authId: true, }) .required(); -export const apiSaveSSHKey = createSchema - .pick({ - sshPrivateKey: true, - }) - .required(); - -export const apiAssignDomain = createSchema - .pick({ - host: true, - certificateType: true, - letsEncryptEmail: true, - https: true, - }) - .required() - .partial({ - letsEncryptEmail: true, - https: true, - }); - -export const apiUpdateDockerCleanup = createSchema - .pick({ - enableDockerCleanup: true, - }) - .required() - .extend({ - serverId: z.string().optional(), - }); export const apiTraefikConfig = z.object({ traefikConfig: z.string().min(1), @@ -298,32 +206,6 @@ export const apiReadStatsLogs = z.object({ .optional(), }); -export const apiUpdateWebServerMonitoring = z.object({ - metricsConfig: z - .object({ - server: z.object({ - refreshRate: z.number().min(2), - port: z.number().min(1), - token: z.string(), - urlCallback: z.string().url(), - retentionDays: z.number().min(1), - cronJob: z.string().min(1), - thresholds: z.object({ - cpu: z.number().min(0), - memory: z.number().min(0), - }), - }), - containers: z.object({ - refreshRate: z.number().min(2), - services: z.object({ - include: z.array(z.string()).optional(), - exclude: z.array(z.string()).optional(), - }), - }), - }) - .required(), -}); - export const apiUpdateUser = createSchema.partial().extend({ email: z .string() @@ -334,29 +216,4 @@ export const apiUpdateUser = createSchema.partial().extend({ currentPassword: z.string().optional(), name: z.string().optional(), lastName: z.string().optional(), - metricsConfig: z - .object({ - server: z.object({ - type: z.enum(["Dokploy", "Remote"]), - refreshRate: z.number(), - port: z.number(), - token: z.string(), - urlCallback: z.string(), - retentionDays: z.number(), - cronJob: z.string(), - thresholds: z.object({ - cpu: z.number(), - memory: z.number(), - }), - }), - containers: z.object({ - refreshRate: z.number(), - services: z.object({ - include: z.array(z.string()), - exclude: z.array(z.string()), - }), - }), - }) - .optional(), - logCleanupCron: z.string().optional().nullable(), }); diff --git a/packages/server/src/db/schema/web-server-settings.ts b/packages/server/src/db/schema/web-server-settings.ts new file mode 100644 index 0000000000..92219091de --- /dev/null +++ b/packages/server/src/db/schema/web-server-settings.ts @@ -0,0 +1,178 @@ +import { relations } from "drizzle-orm"; +import { boolean, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { createInsertSchema } from "drizzle-zod"; +import { nanoid } from "nanoid"; +import { z } from "zod"; +import { certificateType } from "./shared"; + +export const webServerSettings = pgTable("webServerSettings", { + id: text("id") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + // Web Server Configuration + serverIp: text("serverIp"), + certificateType: certificateType("certificateType").notNull().default("none"), + https: boolean("https").notNull().default(false), + host: text("host"), + letsEncryptEmail: text("letsEncryptEmail"), + sshPrivateKey: text("sshPrivateKey"), + enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(true), + logCleanupCron: text("logCleanupCron").default("0 0 * * *"), + // Metrics Configuration + metricsConfig: jsonb("metricsConfig") + .$type<{ + server: { + type: "Dokploy" | "Remote"; + refreshRate: number; + port: number; + token: string; + urlCallback: string; + retentionDays: number; + cronJob: string; + thresholds: { + cpu: number; + memory: number; + }; + }; + containers: { + refreshRate: number; + services: { + include: string[]; + exclude: string[]; + }; + }; + }>() + .notNull() + .default({ + server: { + type: "Dokploy", + refreshRate: 60, + port: 4500, + token: "", + retentionDays: 2, + cronJob: "", + urlCallback: "", + thresholds: { + cpu: 0, + memory: 0, + }, + }, + containers: { + refreshRate: 60, + services: { + include: [], + exclude: [], + }, + }, + }), + // Cache Cleanup Configuration + cleanupCacheApplications: boolean("cleanupCacheApplications") + .notNull() + .default(false), + cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") + .notNull() + .default(false), + cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") + .notNull() + .default(false), + createdAt: timestamp("created_at").defaultNow(), + updatedAt: timestamp("updated_at").notNull().defaultNow(), +}); + +export const webServerSettingsRelations = relations( + webServerSettings, + () => ({}), +); + +const createSchema = createInsertSchema(webServerSettings, { + id: z.string().min(1), +}); + +export const apiUpdateWebServerSettings = createSchema.partial().extend({ + serverIp: z.string().optional(), + certificateType: z.enum(["letsencrypt", "none", "custom"]).optional(), + https: z.boolean().optional(), + host: z.string().optional(), + letsEncryptEmail: z.string().email().optional().nullable(), + sshPrivateKey: z.string().optional(), + enableDockerCleanup: z.boolean().optional(), + logCleanupCron: z.string().optional().nullable(), + metricsConfig: z + .object({ + server: z.object({ + type: z.enum(["Dokploy", "Remote"]), + refreshRate: z.number(), + port: z.number(), + token: z.string(), + urlCallback: z.string(), + retentionDays: z.number(), + cronJob: z.string(), + thresholds: z.object({ + cpu: z.number(), + memory: z.number(), + }), + }), + containers: z.object({ + refreshRate: z.number(), + services: z.object({ + include: z.array(z.string()), + exclude: z.array(z.string()), + }), + }), + }) + .optional(), + cleanupCacheApplications: z.boolean().optional(), + cleanupCacheOnPreviews: z.boolean().optional(), + cleanupCacheOnCompose: z.boolean().optional(), +}); + +export const apiAssignDomain = z + .object({ + host: z.string(), + certificateType: z.enum(["letsencrypt", "none", "custom"]), + letsEncryptEmail: z.string().email().optional().nullable(), + https: z.boolean().optional(), + }) + .required() + .partial({ + letsEncryptEmail: true, + https: true, + }); + +export const apiSaveSSHKey = z + .object({ + sshPrivateKey: z.string(), + }) + .required(); + +export const apiUpdateDockerCleanup = z.object({ + enableDockerCleanup: z.boolean(), + serverId: z.string().optional(), +}); + +export const apiUpdateWebServerMonitoring = z.object({ + metricsConfig: z + .object({ + server: z.object({ + refreshRate: z.number().min(2), + port: z.number().min(1), + token: z.string(), + urlCallback: z.string().url(), + retentionDays: z.number().min(1), + cronJob: z.string().min(1), + thresholds: z.object({ + cpu: z.number().min(0), + memory: z.number().min(0), + }), + }), + containers: z.object({ + refreshRate: z.number().min(2), + services: z.object({ + include: z.array(z.string()).optional(), + exclude: z.array(z.string()).optional(), + }), + }), + }) + .required(), +}); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index e6d7532937..f28711dbf7 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -41,6 +41,7 @@ export * from "./services/settings"; export * from "./services/ssh-key"; export * from "./services/user"; export * from "./services/volume-backups"; +export * from "./services/web-server-settings"; export * from "./setup/config-paths"; export * from "./setup/monitoring-setup"; export * from "./setup/postgres-setup"; diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 9c56a7d59b..d4379e4bef 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -9,7 +9,10 @@ import { IS_CLOUD } from "../constants"; import { db } from "../db"; import * as schema from "../db/schema"; import { getUserByToken } from "../services/admin"; -import { updateUser } from "../services/user"; +import { + getWebServerSettings, + updateWebServerSettings, +} from "../services/web-server-settings"; import { getHubSpotUTK, submitToHubSpot } from "../utils/tracking/hubspot"; import { sendEmail } from "../verification/send-verification-email"; import { getPublicIpWithFallback } from "../wss/utils"; @@ -35,22 +38,14 @@ const { handler, api } = betterAuth({ }, ...(!IS_CLOUD && { async trustedOrigins() { - const admin = await db.query.member.findFirst({ - where: eq(schema.member.role, "owner"), - with: { - user: true, - }, - }); - - if (admin) { - return [ - ...(admin.user.serverIp - ? [`http://${admin.user.serverIp}:3000`] - : []), - ...(admin.user.host ? [`https://${admin.user.host}`] : []), - ]; + const settings = await getWebServerSettings(); + if (!settings) { + return []; } - return []; + return [ + ...(settings?.serverIp ? [`http://${settings?.serverIp}:3000`] : []), + ...(settings?.host ? [`https://${settings?.host}`] : []), + ]; }, }), emailVerification: { @@ -122,7 +117,7 @@ const { handler, api } = betterAuth({ }); if (!IS_CLOUD) { - await updateUser(user.id, { + await updateWebServerSettings({ serverIp: await getPublicIpWithFallback(), }); } diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 0e8612415e..323d0177e8 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -8,6 +8,7 @@ import { import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { IS_CLOUD } from "../constants"; +import { getWebServerSettings } from "./web-server-settings"; export const findUserById = async (userId: string) => { const userResult = await db.query.user.findFirst({ @@ -107,11 +108,11 @@ export const getDokployUrl = async () => { if (IS_CLOUD) { return "https://app.dokploy.com"; } - const owner = await findOwner(); + const settings = await getWebServerSettings(); - if (owner.user.host) { - const protocol = owner.user.https ? "https" : "http"; - return `${protocol}://${owner.user.host}`; + if (settings?.host) { + const protocol = settings?.https ? "https" : "http"; + return `${protocol}://${settings?.host}`; } - return `http://${owner.user.serverIp}:${process.env.PORT}`; + return `http://${settings?.serverIp}:${process.env.PORT}`; }; diff --git a/packages/server/src/services/ai.ts b/packages/server/src/services/ai.ts index 277ec6c391..fefc82eaed 100644 --- a/packages/server/src/services/ai.ts +++ b/packages/server/src/services/ai.ts @@ -6,8 +6,8 @@ import { generateObject } from "ai"; import { desc, eq } from "drizzle-orm"; import { z } from "zod"; import { IS_CLOUD } from "../constants"; -import { findOrganizationById } from "./admin"; import { findServerById } from "./server"; +import { getWebServerSettings } from "./web-server-settings"; export const getAiSettingsByOrganizationId = async (organizationId: string) => { const aiSettings = await db.query.ai.findMany({ @@ -79,8 +79,8 @@ export const suggestVariants = async ({ let ip = ""; if (!IS_CLOUD) { - const organization = await findOrganizationById(organizationId); - ip = organization?.owner.serverIp || ""; + const settings = await getWebServerSettings(); + ip = settings?.serverIp || ""; } if (serverId) { diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts index 50888e546d..e9a21c1f7c 100644 --- a/packages/server/src/services/domain.ts +++ b/packages/server/src/services/domain.ts @@ -3,10 +3,10 @@ import { promisify } from "node:util"; import { db } from "@dokploy/server/db"; import { generateRandomDomain } from "@dokploy/server/templates"; import { manageDomain } from "@dokploy/server/utils/traefik/domain"; +import { getWebServerSettings } from "@dokploy/server/services/web-server-settings"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { type apiCreateDomain, domains } from "../db/schema"; -import { findUserById } from "./admin"; import { findApplicationById } from "./application"; import { detectCDNProvider } from "./cdn"; import { findServerById } from "./server"; @@ -61,9 +61,9 @@ export const generateTraefikMeDomain = async ( projectName: appName, }); } - const admin = await findUserById(userId); + const settings = await getWebServerSettings(); return generateRandomDomain({ - serverIp: admin?.serverIp || "", + serverIp: settings?.serverIp || "", projectName: appName, }); }; diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index 5ee763b089..1ece3bc539 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -13,11 +13,11 @@ import { removeDirectoryCode } from "../utils/filesystem/directory"; import { authGithub } from "../utils/providers/github"; import { removeTraefikConfig } from "../utils/traefik/application"; import { manageDomain } from "../utils/traefik/domain"; -import { findUserById } from "./admin"; import { findApplicationById } from "./application"; import { removeDeploymentsByPreviewDeploymentId } from "./deployment"; import { createDomain } from "./domain"; import { type Github, getIssueComment } from "./github"; +import { getWebServerSettings } from "./web-server-settings"; export type PreviewDeployment = typeof previewDeployments.$inferSelect; @@ -253,8 +253,8 @@ const generateWildcardDomain = async ( } if (!ip) { - const admin = await findUserById(userId); - ip = admin?.serverIp || ""; + const settings = await getWebServerSettings(); + ip = settings?.serverIp || ""; } const slugIp = ip.replaceAll(".", "-"); diff --git a/packages/server/src/services/web-server-settings.ts b/packages/server/src/services/web-server-settings.ts new file mode 100644 index 0000000000..289d119c99 --- /dev/null +++ b/packages/server/src/services/web-server-settings.ts @@ -0,0 +1,44 @@ +import { db } from "@dokploy/server/db"; +import { webServerSettings } from "@dokploy/server/db/schema"; +import { eq } from "drizzle-orm"; + +/** + * Get the web server settings (singleton - only one row should exist) + */ +export const getWebServerSettings = async () => { + const settings = await db.query.webServerSettings.findFirst({ + orderBy: (settings, { asc }) => [asc(settings.createdAt)], + }); + + if (!settings) { + // Create default settings if none exist + const [newSettings] = await db + .insert(webServerSettings) + .values({}) + .returning(); + + return newSettings; + } + + return settings; +}; + +/** + * Update web server settings + */ +export const updateWebServerSettings = async ( + updates: Partial, +) => { + const current = await getWebServerSettings(); + + const [updated] = await db + .update(webServerSettings) + .set({ + ...updates, + updatedAt: new Date(), + }) + .where(eq(webServerSettings.id, current?.id ?? "")) + .returning(); + + return updated; +}; diff --git a/packages/server/src/setup/monitoring-setup.ts b/packages/server/src/setup/monitoring-setup.ts index 20055be9ae..287894c999 100644 --- a/packages/server/src/setup/monitoring-setup.ts +++ b/packages/server/src/setup/monitoring-setup.ts @@ -1,7 +1,7 @@ import { findServerById } from "@dokploy/server/services/server"; +import { getWebServerSettings } from "@dokploy/server/services/web-server-settings"; import type { ContainerCreateOptions } from "dockerode"; import { IS_CLOUD } from "../constants"; -import { findUserById } from "../services/admin"; import { getDokployImageTag } from "../services/settings"; import { pullImage, pullRemoteImage } from "../utils/docker/utils"; import { execAsync, execAsyncRemote } from "../utils/process/execAsync"; @@ -83,8 +83,8 @@ export const setupMonitoring = async (serverId: string) => { } }; -export const setupWebMonitoring = async (userId: string) => { - const user = await findUserById(userId); +export const setupWebMonitoring = async () => { + const webServerSettings = await getWebServerSettings(); const containerName = "dokploy-monitoring"; let imageName = "dokploy/monitoring:latest"; @@ -99,7 +99,7 @@ export const setupWebMonitoring = async (userId: string) => { const settings: ContainerCreateOptions = { name: containerName, - Env: [`METRICS_CONFIG=${JSON.stringify(user?.metricsConfig)}`], + Env: [`METRICS_CONFIG=${JSON.stringify(webServerSettings?.metricsConfig)}`], Image: imageName, HostConfig: { // Memory: 100 * 1024 * 1024, // 100MB en bytes @@ -110,9 +110,9 @@ export const setupWebMonitoring = async (userId: string) => { Name: "always", }, PortBindings: { - [`${user?.metricsConfig?.server?.port}/tcp`]: [ + [`${webServerSettings?.metricsConfig?.server?.port}/tcp`]: [ { - HostPort: user?.metricsConfig?.server?.port.toString(), + HostPort: webServerSettings?.metricsConfig?.server?.port.toString(), }, ], }, @@ -126,7 +126,7 @@ export const setupWebMonitoring = async (userId: string) => { // NetworkMode: "host", }, ExposedPorts: { - [`${user?.metricsConfig?.server?.port}/tcp`]: {}, + [`${webServerSettings?.metricsConfig?.server?.port}/tcp`]: {}, }, }; const docker = await getRemoteDocker(); diff --git a/packages/server/src/templates/processors.ts b/packages/server/src/templates/processors.ts index 9e73fb1f45..ce1553095f 100644 --- a/packages/server/src/templates/processors.ts +++ b/packages/server/src/templates/processors.ts @@ -170,12 +170,12 @@ export function processValue( } // If not a utility function, try to get from variables - return variables[varName] || match; + return varName in variables ? (variables[varName] ?? match) : match; }); // Then replace any remaining ${var} with their values from variables processedValue = processedValue.replace(/\${([^}]+)}/g, (match, varName) => { - return variables[varName] || match; + return varName in variables ? (variables[varName] ?? match) : match; }); return processedValue; diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index 237a68f170..13a52c4b1c 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -1,6 +1,8 @@ import { paths } from "@dokploy/server/constants"; -import { findOwner } from "@dokploy/server/services/admin"; -import { updateUser } from "@dokploy/server/services/user"; +import { + getWebServerSettings, + updateWebServerSettings, +} from "@dokploy/server/services/web-server-settings"; import { scheduledJobs, scheduleJob } from "node-schedule"; import { execAsync } from "../process/execAsync"; @@ -29,12 +31,9 @@ export const startLogCleanup = async ( } }); - const owner = await findOwner(); - if (owner) { - await updateUser(owner.user.id, { - logCleanupCron: cronExpression, - }); - } + await updateWebServerSettings({ + logCleanupCron: cronExpression, + }); return true; } catch (error) { @@ -51,12 +50,9 @@ export const stopLogCleanup = async (): Promise => { } // Update database - const owner = await findOwner(); - if (owner) { - await updateUser(owner.user.id, { - logCleanupCron: null, - }); - } + await updateWebServerSettings({ + logCleanupCron: null, + }); return true; } catch (error) { @@ -69,8 +65,8 @@ export const getLogCleanupStatus = async (): Promise<{ enabled: boolean; cronExpression: string | null; }> => { - const owner = await findOwner(); - const cronExpression = owner?.user.logCleanupCron ?? null; + const settings = await getWebServerSettings(); + const cronExpression = settings?.logCleanupCron ?? null; return { enabled: cronExpression !== null, cronExpression, diff --git a/packages/server/src/utils/backups/index.ts b/packages/server/src/utils/backups/index.ts index dfdcd2cac9..14d38ddf07 100644 --- a/packages/server/src/utils/backups/index.ts +++ b/packages/server/src/utils/backups/index.ts @@ -2,6 +2,7 @@ import path from "node:path"; import { member } from "@dokploy/server/db/schema"; import type { BackupSchedule } from "@dokploy/server/services/backup"; import { getAllServers } from "@dokploy/server/services/server"; +import { getWebServerSettings } from "@dokploy/server/services/web-server-settings"; import { eq } from "drizzle-orm"; import { scheduleJob } from "node-schedule"; import { db } from "../../db/index"; @@ -25,7 +26,9 @@ export const initCronJobs = async () => { return; } - if (admin?.user?.enableDockerCleanup) { + const webServerSettings = await getWebServerSettings(); + + if (webServerSettings?.enableDockerCleanup) { scheduleJob("docker-cleanup", "0 0 * * *", async () => { console.log( `Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`, @@ -82,9 +85,12 @@ export const initCronJobs = async () => { } } - if (admin?.user?.logCleanupCron) { - console.log("Starting log requests cleanup", admin.user.logCleanupCron); - await startLogCleanup(admin.user.logCleanupCron); + if (webServerSettings?.logCleanupCron) { + console.log( + "Starting log requests cleanup", + webServerSettings.logCleanupCron, + ); + await startLogCleanup(webServerSettings.logCleanupCron); } }; diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts index fe5417ea57..5a0184a3d9 100644 --- a/packages/server/src/utils/builders/compose.ts +++ b/packages/server/src/utils/builders/compose.ts @@ -90,7 +90,7 @@ export const createCommand = (compose: ComposeNested) => { if (composeType === "docker-compose") { command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`; } else if (composeType === "stack") { - command = `stack deploy -c ${path} ${appName} --prune`; + command = `stack deploy -c ${path} ${appName} --prune --with-registry-auth`; } return command; diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts index e2cf4a4a4b..aa014a05c8 100644 --- a/packages/server/src/utils/cluster/upload.ts +++ b/packages/server/src/utils/cluster/upload.ts @@ -117,7 +117,7 @@ const getRegistryCommands = ( ): string => { return ` echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ; -echo "${registry.password}" | docker login ${registry.registryUrl} -u ${registry.username} --password-stdin || { +echo "${registry.password}" | docker login ${registry.registryUrl} -u '${registry.username}' --password-stdin || { echo "❌ DockerHub Failed" ; exit 1; } diff --git a/packages/server/src/utils/databases/mongo.ts b/packages/server/src/utils/databases/mongo.ts index 9cb8a69f4b..556878fe25 100644 --- a/packages/server/src/utils/databases/mongo.ts +++ b/packages/server/src/utils/databases/mongo.ts @@ -54,7 +54,7 @@ if [ "$REPLICA_STATUS" != "1" ]; then mongosh --eval ' rs.initiate({ _id: "rs0", - members: [{ _id: 0, host: "localhost:27017", priority: 1 }] + members: [{ _id: 0, host: "${appName}:27017", priority: 1 }] }); // Wait for the replica set to initialize diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index d674a88401..045040061a 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -146,17 +146,17 @@ export const getContainerByName = (name: string): Promise => { }; /** - * Docker commands passed through this method are held during Docker's build or pull process. + * Docker commands sent using this method are held in a hold when Docker is busy. * * https://github.com/Dokploy/dokploy/pull/3064 - * https://github.com/fir4tozden */ -export const dockerSafeExec = (exec: string) => `CHECK_INTERVAL=10 +export const dockerSafeExec = (exec: string) => ` +CHECK_INTERVAL=10 echo "Preparing for execution..." while true; do - PROCESSES=$(ps aux | grep -E "docker build|docker pull" | grep -v grep) + PROCESSES=$(ps aux | grep -E "^.*docker [A-Za-z]" | grep -v grep) if [ -z "$PROCESSES" ]; then echo "Docker is idle. Starting execution..." @@ -169,14 +169,15 @@ done ${exec} -echo "Execution completed."`; +echo "Execution completed." +`; const cleanupCommands = { containers: "docker container prune --force", images: "docker image prune --all --force", + volumes: "docker volume prune --all --force", builders: "docker builder prune --all --force", system: "docker system prune --all --force", - volumes: "docker volume prune --all --force", }; export const cleanupContainers = async (serverId?: string) => { @@ -257,24 +258,48 @@ export const cleanupSystem = async (serverId?: string) => { } }; +/** + * Volume cleanup should always be performed manually by the user. The reason is that during automatic cleanup, a volume may be deleted due to a stopped container, which is a dangerous situation. + * + * https://github.com/Dokploy/dokploy/pull/3267 + */ +const excludedCleanupAllCommands: (keyof typeof cleanupCommands)[] = [ + "volumes", +]; + export const cleanupAll = async (serverId?: string) => { - await cleanupContainers(serverId); - await cleanupImages(serverId); - await cleanupBuilders(serverId); - await cleanupSystem(serverId); + for (const [key, command] of Object.entries(cleanupCommands) as [ + keyof typeof cleanupCommands, + string, + ][]) { + if (excludedCleanupAllCommands.includes(key)) continue; + + try { + if (serverId) { + await execAsyncRemote(serverId, dockerSafeExec(command)); + } else { + await execAsync(dockerSafeExec(command)); + } + } catch {} + } }; export const cleanupAllBackground = async (serverId?: string) => { Promise.allSettled( - Object.values(cleanupCommands).map(async (command) => { - try { + ( + Object.entries(cleanupCommands) as [ + keyof typeof cleanupCommands, + string, + ][] + ) + .filter(([key]) => !excludedCleanupAllCommands.includes(key)) + .map(async ([, command]) => { if (serverId) { await execAsyncRemote(serverId, dockerSafeExec(command)); } else { await execAsync(dockerSafeExec(command)); } - } catch (error) {} - }), + }), ) .then((results) => { const failed = results.filter((r) => r.status === "rejected"); diff --git a/packages/server/src/utils/traefik/web-server.ts b/packages/server/src/utils/traefik/web-server.ts index 0209d9a212..e5315dab44 100644 --- a/packages/server/src/utils/traefik/web-server.ts +++ b/packages/server/src/utils/traefik/web-server.ts @@ -1,7 +1,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; -import type { User } from "@dokploy/server/services/user"; +import type { webServerSettings } from "@dokploy/server/db/schema/web-server-settings"; import { parse, stringify } from "yaml"; import { loadOrCreateConfig, @@ -12,10 +12,10 @@ import type { FileConfig } from "./file-types"; import type { MainTraefikConfig } from "./types"; export const updateServerTraefik = ( - user: User | null, + settings: typeof webServerSettings.$inferSelect | null, newHost: string | null, ) => { - const { https, certificateType } = user || {}; + const { https, certificateType } = settings || {}; const appName = "dokploy"; const config: FileConfig = loadOrCreateConfig(appName); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1d8e5c0dc..b0c06df37e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -406,9 +406,6 @@ importers: recharts: specifier: ^2.15.3 version: 2.15.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rotating-file-stream: - specifier: 3.2.3 - version: 3.2.3 shell-quote: specifier: ^1.8.1 version: 1.8.2 @@ -732,9 +729,6 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) - rotating-file-stream: - specifier: 3.2.3 - version: 3.2.3 shell-quote: specifier: ^1.8.1 version: 1.8.2 @@ -7064,10 +7058,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rotating-file-stream@3.2.3: - resolution: {integrity: sha512-cfmm3tqdnbuYw2FBmRTPBDaohYEbMJ3211T35o6eZdr4d7v69+ZeK1Av84Br7FLj2dlzyeZSbN6qTuXXE6dawQ==} - engines: {node: '>=14.0'} - rou3@0.5.1: resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} @@ -14660,8 +14650,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.41.1 fsevents: 2.3.3 - rotating-file-stream@3.2.3: {} - rou3@0.5.1: {} run-parallel@1.2.0: