diff --git a/apps/dokploy/__test__/schedules/database-schedule-access.test.ts b/apps/dokploy/__test__/schedules/database-schedule-access.test.ts new file mode 100644 index 00000000000..82aef20ffc1 --- /dev/null +++ b/apps/dokploy/__test__/schedules/database-schedule-access.test.ts @@ -0,0 +1,142 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + findDatabase: { + postgres: vi.fn(), + mysql: vi.fn(), + mariadb: vi.fn(), + mongo: vi.fn(), + redis: vi.fn(), + libsql: vi.fn(), + }, + checkServicePermissionAndAccess: vi.fn(), +})); + +vi.mock("@trpc/server", () => ({ + TRPCError: class extends Error { + code: string; + + constructor({ code, message }: { code: string; message: string }) { + super(message); + this.code = code; + } + }, +})); +vi.mock("@dokploy/server/services/postgres", () => ({ + findPostgresById: mocks.findDatabase.postgres, +})); +vi.mock("@dokploy/server/services/mysql", () => ({ + findMySqlById: mocks.findDatabase.mysql, +})); +vi.mock("@dokploy/server/services/mariadb", () => ({ + findMariadbById: mocks.findDatabase.mariadb, +})); +vi.mock("@dokploy/server/services/mongo", () => ({ + findMongoById: mocks.findDatabase.mongo, +})); +vi.mock("@dokploy/server/services/redis", () => ({ + findRedisById: mocks.findDatabase.redis, +})); +vi.mock("@dokploy/server/services/libsql", () => ({ + findLibsqlById: mocks.findDatabase.libsql, +})); +vi.mock("@dokploy/server/services/permission", () => ({ + checkServicePermissionAndAccess: mocks.checkServicePermissionAndAccess, +})); + +const { checkDatabaseScheduleAccess } = await import( + "@/server/api/utils/database-schedule-access" +); + +const ctx = { + user: { id: "user-1" }, + session: { activeOrganizationId: "org-1" }, +}; + +beforeEach(() => { + vi.resetAllMocks(); + for (const findDatabase of Object.values(mocks.findDatabase)) { + findDatabase.mockResolvedValue({ + environment: { project: { organizationId: "org-1" } }, + }); + } +}); + +describe.each([ + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", +] as const)("%s schedule permissions", (databaseType) => { + it.each(["create", "read", "update", "delete"] as const)( + "checks the %s schedule permission and service access", + async (action) => { + const permissions = { schedule: [action] }; + await checkDatabaseScheduleAccess( + ctx, + databaseType, + "database-1", + permissions, + ); + + expect( + mocks.checkServicePermissionAndAccess, + ).toHaveBeenCalledExactlyOnceWith(ctx, "database-1", permissions); + for (const [type, findDatabase] of Object.entries(mocks.findDatabase)) { + if (type === databaseType) { + expect(findDatabase).toHaveBeenCalledExactlyOnceWith("database-1"); + } else { + expect(findDatabase).not.toHaveBeenCalled(); + } + } + }, + ); + + it("does not require read permission when cancelling a run", async () => { + mocks.checkServicePermissionAndAccess.mockImplementation( + async (_ctx, _id, permissions) => { + if (permissions.deployment.includes("read")) { + throw new Error("Read access denied"); + } + }, + ); + await checkDatabaseScheduleAccess(ctx, databaseType, "database-1", { + deployment: ["cancel"], + }); + + expect( + mocks.checkServicePermissionAndAccess, + ).toHaveBeenCalledExactlyOnceWith(ctx, "database-1", { + deployment: ["cancel"], + }); + }); + + it("rejects databases belonging to a different organization", async () => { + mocks.findDatabase[databaseType].mockResolvedValue({ + environment: { project: { organizationId: "another-org" } }, + }); + + await expect( + checkDatabaseScheduleAccess(ctx, databaseType, "database-1", { + schedule: ["read"], + }), + ).rejects.toMatchObject({ code: "UNAUTHORIZED" }); + }); + + it("rejects permission failures before looking up the database", async () => { + mocks.checkServicePermissionAndAccess.mockRejectedValue( + new Error("Access denied"), + ); + + await expect( + checkDatabaseScheduleAccess(ctx, databaseType, "database-1", { + schedule: ["read"], + }), + ).rejects.toThrow("Access denied"); + for (const findDatabase of Object.values(mocks.findDatabase)) { + expect(findDatabase).not.toHaveBeenCalled(); + } + }); +}); diff --git a/apps/dokploy/__test__/schedules/database-schedule-deletion.test.ts b/apps/dokploy/__test__/schedules/database-schedule-deletion.test.ts new file mode 100644 index 00000000000..4a30609c6b1 --- /dev/null +++ b/apps/dokploy/__test__/schedules/database-schedule-deletion.test.ts @@ -0,0 +1,145 @@ +import { + databaseScheduleIdColumns, + databaseScheduleTypes, + schedules, +} from "@dokploy/server/db/schema/schedule"; +import { eq } from "drizzle-orm"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + findMany: vi.fn(), + removeJob: vi.fn(), + removeScheduleJob: vi.fn(), + mode: { cloud: true }, +})); + +vi.mock("@dokploy/server", () => ({ + get IS_CLOUD() { + return mocks.mode.cloud; + }, + removeScheduleJob: mocks.removeScheduleJob, +})); +vi.mock("@dokploy/server/db", () => ({ + db: { query: { schedules: { findMany: mocks.findMany } } }, +})); +vi.mock("@/server/utils/backup", () => ({ removeJob: mocks.removeJob })); + +const { cancelDatabaseScheduleJobs, cancelProjectDatabaseScheduleJobs } = + await import("@/server/utils/database-schedules"); + +beforeEach(() => { + vi.resetAllMocks(); + mocks.mode.cloud = true; + mocks.removeJob.mockResolvedValue({ result: true }); + mocks.findMany.mockResolvedValue([ + { + scheduleId: "schedule-1", + cronExpression: "0 9 * * *", + timezone: "Australia/Sydney", + }, + { + scheduleId: "schedule-2", + cronExpression: "0 10 * * *", + timezone: null, + }, + ]); +}); + +describe.each(databaseScheduleTypes)("%s database deletion", (databaseType) => { + it("unregisters every cloud job with its original cron and timezone", async () => { + await cancelDatabaseScheduleJobs(databaseType, "database-1"); + + expect(mocks.findMany).toHaveBeenCalledExactlyOnceWith({ + where: eq( + schedules[databaseScheduleIdColumns[databaseType]], + "database-1", + ), + columns: { + scheduleId: true, + cronExpression: true, + timezone: true, + }, + }); + expect(mocks.removeJob).toHaveBeenNthCalledWith(1, { + type: "schedule", + scheduleId: "schedule-1", + cronSchedule: "0 9 * * *", + timezone: "Australia/Sydney", + }); + expect(mocks.removeJob).toHaveBeenNthCalledWith(2, { + type: "schedule", + scheduleId: "schedule-2", + cronSchedule: "0 10 * * *", + timezone: null, + }); + expect(mocks.removeScheduleJob).not.toHaveBeenCalled(); + }); + + it("cancels self-hosted jobs instead of contacting the cloud worker", async () => { + mocks.mode.cloud = false; + await cancelDatabaseScheduleJobs(databaseType, "database-1"); + + expect(mocks.removeScheduleJob).toHaveBeenNthCalledWith(1, "schedule-1"); + expect(mocks.removeScheduleJob).toHaveBeenNthCalledWith(2, "schedule-2"); + expect(mocks.removeJob).not.toHaveBeenCalled(); + }); +}); + +it("does not silently continue deletion when the cloud scheduler is unavailable", async () => { + mocks.removeJob.mockRejectedValueOnce(new Error("Queue unavailable")); + + await expect( + cancelDatabaseScheduleJobs("postgres", "database-1"), + ).rejects.toThrow("Queue unavailable"); + // Callers must await cancellation before removing the database row. + // A rejected cancellation keeps its FK and schedule rows intact. + expect(mocks.removeJob).toHaveBeenCalledTimes(1); +}); + +it("rejects a cloud scheduler HTTP error body instead of deleting the database", async () => { + // removeJob returns parsed JSON without checking Response.ok. + mocks.removeJob.mockResolvedValueOnce({ message: "Invalid API Key" }); + + await expect( + cancelDatabaseScheduleJobs("postgres", "database-1"), + ).rejects.toThrow( + "Cloud scheduler did not confirm removal of database schedule schedule-1", + ); + expect(mocks.removeJob).toHaveBeenCalledTimes(1); +}); + +it("accepts a confirmed already-absent cloud job", async () => { + // Disabled schedules are included in cleanup, but may not be registered. + mocks.removeJob.mockResolvedValueOnce({ result: false }); + + await expect( + cancelDatabaseScheduleJobs("postgres", "database-1"), + ).resolves.toBeUndefined(); + expect(mocks.removeJob).toHaveBeenCalledTimes(2); +}); + +it("cancels database jobs when a project is cascade-deleted", async () => { + mocks.findMany.mockResolvedValue([]); + const environment = Object.fromEntries( + databaseScheduleTypes.map((type) => [ + type, + [{ [databaseScheduleIdColumns[type]]: `database-${type}` }], + ]), + ); + + await cancelProjectDatabaseScheduleJobs([ + environment, + ] as unknown as Parameters[0]); + + expect(mocks.findMany).toHaveBeenCalledTimes(databaseScheduleTypes.length); + for (const type of databaseScheduleTypes) { + expect(mocks.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + where: eq( + schedules[databaseScheduleIdColumns[type]], + `database-${type}`, + ), + }), + ); + } +}); diff --git a/apps/dokploy/__test__/schedules/database-schedule-remote-logs.test.ts b/apps/dokploy/__test__/schedules/database-schedule-remote-logs.test.ts new file mode 100644 index 00000000000..2c9db360bfc --- /dev/null +++ b/apps/dokploy/__test__/schedules/database-schedule-remote-logs.test.ts @@ -0,0 +1,217 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + findDeploymentById: vi.fn(), + findScheduleById: vi.fn(), + getScheduleServerId: vi.fn(), + execAsyncRemote: vi.fn(), + execAsync: vi.fn(), + checkDatabaseScheduleAccess: vi.fn(), + checkServicePermissionAndAccess: vi.fn(), + removeDeployment: vi.fn(), + getDatabaseScheduleId: vi.fn(), + isDatabaseScheduleType: vi.fn(), +})); + +vi.mock("@trpc/server", () => ({ TRPCError: Error })); +vi.mock("@dokploy/server/db/schema/schedule", () => ({ + getDatabaseScheduleId: mocks.getDatabaseScheduleId, + isDatabaseScheduleType: mocks.isDatabaseScheduleType, +})); +vi.mock("@dokploy/server", () => ({ + findDeploymentById: mocks.findDeploymentById, + findScheduleById: mocks.findScheduleById, + getScheduleServerId: mocks.getScheduleServerId, + execAsyncRemote: mocks.execAsyncRemote, + execAsync: mocks.execAsync, + removeDeployment: mocks.removeDeployment, + IS_CLOUD: true, +})); +vi.mock("@dokploy/server/db", () => ({ db: {} })); +vi.mock("@dokploy/server/services/permission", () => ({ + checkServicePermissionAndAccess: mocks.checkServicePermissionAndAccess, + findMemberByUserId: vi.fn(), +})); +vi.mock("@dokploy/server/services/server", () => ({ + findServerById: vi.fn(), +})); +vi.mock("@/server/api/utils/database-schedule-access", () => ({ + checkDatabaseScheduleAccess: mocks.checkDatabaseScheduleAccess, +})); +vi.mock("@/server/api/utils/audit", () => ({ audit: vi.fn() })); +vi.mock("@/server/db/schema", () => ({ + apiFindAllByApplication: {}, + apiFindAllByCompose: {}, + apiFindAllByServer: {}, + apiFindAllByType: {}, + deployments: {}, + server: {}, +})); +vi.mock("@/server/queues/queueSetup", () => ({ myQueue: {} })); +vi.mock("@/server/utils/deploy", () => ({ fetchDeployApiJobs: vi.fn() })); +vi.mock("@/server/api/trpc", () => { + const procedure = { + input: () => ({ + query: (handler: unknown) => handler, + mutation: (handler: unknown) => handler, + }), + query: (handler: unknown) => handler, + mutation: (handler: unknown) => handler, + }; + return { + createTRPCRouter: (routes: unknown) => routes, + protectedProcedure: procedure, + withPermission: () => procedure, + }; +}); + +const { deploymentRouter } = await import("@/server/api/routers/deployment"); +const readLogs = deploymentRouter.readLogs as unknown as (args: { + input: { deploymentId: string; tail: number }; + ctx: typeof ctx; +}) => Promise; +const removeDeployment = + deploymentRouter.removeDeployment as unknown as (args: { + input: { deploymentId: string }; + ctx: typeof ctx; + }) => Promise; + +const ctx = { + user: { id: "user-1" }, + session: { activeOrganizationId: "org-1" }, +}; + +beforeEach(() => { + vi.resetAllMocks(); + mocks.getDatabaseScheduleId.mockReturnValue(null); + mocks.isDatabaseScheduleType.mockReturnValue(false); + mocks.execAsyncRemote.mockResolvedValue({ stdout: "remote log output" }); + mocks.execAsync.mockResolvedValue({ stdout: "local log output" }); +}); + +describe("scheduled deployment log server selection", () => { + it.each([ + ["postgres", "postgresId"], + ["mysql", "mysqlId"], + ["mariadb", "mariadbId"], + ["mongo", "mongoId"], + ["redis", "redisId"], + ["libsql", "libsqlId"], + ] as const)( + "reads %s scheduled logs from the database's remote server", + async (type, idColumn) => { + mocks.getDatabaseScheduleId.mockReturnValue("database-1"); + mocks.isDatabaseScheduleType.mockReturnValue(true); + mocks.findDeploymentById.mockResolvedValue({ + deploymentId: "run-1", + logPath: "/logs/db.log", + serverId: null, + application: null, + compose: null, + schedule: { + scheduleId: "schedule-1", + scheduleType: type, + [idColumn]: "database-1", + serverId: null, + }, + }); + const expandedSchedule = { + scheduleId: "schedule-1", + [type]: { serverId: "server-1" }, + }; + mocks.findScheduleById.mockResolvedValue(expandedSchedule); + mocks.getScheduleServerId.mockReturnValue("server-1"); + + const logs = await readLogs({ + input: { deploymentId: "run-1", tail: 50 }, + ctx, + }); + + expect(logs).toBe("remote log output"); + expect(mocks.checkDatabaseScheduleAccess).toHaveBeenCalledWith( + ctx, + type, + "database-1", + { deployment: ["read"] }, + ); + expect(mocks.findScheduleById).toHaveBeenCalledWith("schedule-1"); + expect(mocks.getScheduleServerId).toHaveBeenCalledWith(expandedSchedule); + expect(mocks.execAsyncRemote).toHaveBeenCalledWith( + "server-1", + expect.stringContaining('tail -n 50 "/logs/db.log"'), + ); + expect(mocks.execAsync).not.toHaveBeenCalled(); + }, + ); + + it("checks database read permission before resolving remote log storage", async () => { + mocks.getDatabaseScheduleId.mockReturnValue("database-1"); + mocks.isDatabaseScheduleType.mockReturnValue(true); + mocks.findDeploymentById.mockResolvedValue({ + deploymentId: "run-1", + logPath: "/logs/db.log", + schedule: { + scheduleId: "schedule-1", + scheduleType: "postgres", + postgresId: "database-1", + }, + }); + mocks.checkDatabaseScheduleAccess.mockRejectedValue( + new Error("Access denied"), + ); + + await expect( + readLogs({ input: { deploymentId: "run-1", tail: 50 }, ctx }), + ).rejects.toThrow("Access denied"); + expect(mocks.findScheduleById).not.toHaveBeenCalled(); + expect(mocks.execAsyncRemote).not.toHaveBeenCalled(); + }); + + it("requires only deployment cancel permission to remove a database run", async () => { + mocks.getDatabaseScheduleId.mockReturnValue("database-1"); + mocks.isDatabaseScheduleType.mockReturnValue(true); + mocks.findDeploymentById.mockResolvedValue({ + deploymentId: "run-1", + schedule: { + scheduleId: "schedule-1", + scheduleType: "postgres", + postgresId: "database-1", + }, + }); + + await removeDeployment({ input: { deploymentId: "run-1" }, ctx }); + + expect(mocks.checkDatabaseScheduleAccess).toHaveBeenCalledExactlyOnceWith( + ctx, + "postgres", + "database-1", + { deployment: ["cancel"] }, + ); + expect(mocks.checkServicePermissionAndAccess).not.toHaveBeenCalled(); + expect(mocks.removeDeployment).toHaveBeenCalledWith("run-1"); + }); + + it.each(["application", "compose"] as const)( + "does not change %s deployment log routing", + async (type) => { + mocks.findDeploymentById.mockResolvedValue({ + deploymentId: "run-2", + logPath: "/logs/service.log", + serverId: null, + schedule: null, + application: + type === "application" ? { serverId: "service-server" } : null, + compose: type === "compose" ? { serverId: "service-server" } : null, + }); + + expect( + await readLogs({ input: { deploymentId: "run-2", tail: 20 }, ctx }), + ).toBe("remote log output"); + expect(mocks.findScheduleById).not.toHaveBeenCalled(); + expect(mocks.execAsyncRemote).toHaveBeenCalledWith( + "service-server", + expect.stringContaining('tail -n 20 "/logs/service.log"'), + ); + }, + ); +}); diff --git a/apps/dokploy/__test__/schedules/database-schedule-secret-projection.test.ts b/apps/dokploy/__test__/schedules/database-schedule-secret-projection.test.ts new file mode 100644 index 00000000000..04ac0ff7503 --- /dev/null +++ b/apps/dokploy/__test__/schedules/database-schedule-secret-projection.test.ts @@ -0,0 +1,75 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + findFirst: vi.fn(), +})); + +vi.mock("@dokploy/server/db", () => ({ + db: { query: { schedules: { findFirst: mocks.findFirst } } }, +})); +vi.mock("@dokploy/server/constants", () => ({ + IS_CLOUD: false, + paths: vi.fn(), +})); +vi.mock("@dokploy/server/services/permission", () => ({ + findMemberByUserId: vi.fn(), +})); +vi.mock("@dokploy/server/services/server", () => ({ + findServerById: vi.fn(), +})); +vi.mock("@dokploy/server/utils/docker/utils", () => ({ + encodeBase64: vi.fn(), +})); +vi.mock("@dokploy/server/utils/process/execAsync", () => ({ + execAsync: vi.fn(), + execAsyncRemote: vi.fn(), +})); + +const { findScheduleById, findScheduleOrganizationId, getScheduleServerId } = + await import("@dokploy/server/services/schedule"); + +beforeEach(() => { + vi.resetAllMocks(); +}); + +describe.each([ + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", +] as const)("%s schedule read projection", (databaseType) => { + it("never selects database credentials or environment secrets", async () => { + mocks.findFirst.mockResolvedValue({ + scheduleId: "schedule-1", + [databaseType]: { + name: "database", + appName: "database-container", + serverId: "server-1", + environment: { + environmentId: "environment-1", + project: { organizationId: "org-1" }, + }, + }, + }); + + const schedule = await findScheduleById("schedule-1"); + const query = mocks.findFirst.mock.lastCall?.[0]; + expect(query.with[databaseType]).toEqual({ + columns: { + name: true, + appName: true, + serverId: true, + }, + with: { + environment: { + columns: { environmentId: true }, + with: { project: { columns: { organizationId: true } } }, + }, + }, + }); + expect(getScheduleServerId(schedule)).toBe("server-1"); + expect(await findScheduleOrganizationId("schedule-1")).toBe("org-1"); + }); +}); diff --git a/apps/dokploy/__test__/schedules/database-scheduled-jobs.test.ts b/apps/dokploy/__test__/schedules/database-scheduled-jobs.test.ts new file mode 100644 index 00000000000..eeed367f077 --- /dev/null +++ b/apps/dokploy/__test__/schedules/database-scheduled-jobs.test.ts @@ -0,0 +1,88 @@ +import { + createScheduleSchema, + databaseScheduleIdColumns, + databaseScheduleTypes, + getDatabaseScheduleId, + isDatabaseScheduleType, + isValidDatabaseScheduleTarget, +} from "@dokploy/server/db/schema/schedule"; +import { describe, expect, it } from "vitest"; + +describe("database scheduled job targets", () => { + it.each(databaseScheduleTypes)( + "recognises %s and its unique database foreign key", + (databaseType) => { + const column = databaseScheduleIdColumns[databaseType]; + const target = { scheduleType: databaseType, [column]: "db-123" }; + expect(isDatabaseScheduleType(databaseType)).toBe(true); + expect(isValidDatabaseScheduleTarget(target)).toBe(true); + expect(getDatabaseScheduleId(target)).toBe("db-123"); + expect( + createScheduleSchema.safeParse({ + name: "Refresh data", + cronExpression: "0 0 * * *", + command: "echo hello", + ...target, + }).success, + ).toBe(true); + }, + ); + + it.each(databaseScheduleTypes)( + "rejects missing, mixed and wrong database targets for %s", + (databaseType) => { + const column = databaseScheduleIdColumns[databaseType]; + const otherType = databaseScheduleTypes.find( + (type) => type !== databaseType, + ); + expect(otherType).toBeDefined(); + const otherColumn = databaseScheduleIdColumns[otherType!]; + expect( + isValidDatabaseScheduleTarget({ scheduleType: databaseType }), + ).toBe(false); + expect( + isValidDatabaseScheduleTarget({ + scheduleType: databaseType, + [otherColumn]: "different-db", + }), + ).toBe(false); + expect( + isValidDatabaseScheduleTarget({ + scheduleType: databaseType, + [column]: "db-123", + [otherColumn]: "different-db", + }), + ).toBe(false); + for (const otherTarget of [ + { applicationId: "app-123" }, + { composeId: "compose-123" }, + { serverId: "server-123" }, + { organizationId: "org-123" }, + ]) { + expect( + isValidDatabaseScheduleTarget({ + scheduleType: databaseType, + [column]: "db-123", + ...otherTarget, + }), + ).toBe(false); + } + }, + ); + + it.each(["application", "compose", "server", "dokploy-server"] as const)( + "does not permit database foreign keys on %s schedules", + (scheduleType) => { + expect(isDatabaseScheduleType(scheduleType)).toBe(false); + expect(isValidDatabaseScheduleTarget({ scheduleType })).toBe(true); + for (const column of Object.values(databaseScheduleIdColumns)) { + expect( + isValidDatabaseScheduleTarget({ scheduleType, [column]: "db-123" }), + ).toBe(false); + } + expect( + getDatabaseScheduleId({ scheduleType, postgresId: "db-123" }), + ).toBeNull(); + }, + ); +}); diff --git a/apps/dokploy/__test__/schedules/initialize-scheduled-jobs.test.ts b/apps/dokploy/__test__/schedules/initialize-scheduled-jobs.test.ts new file mode 100644 index 00000000000..e305b471a1d --- /dev/null +++ b/apps/dokploy/__test__/schedules/initialize-scheduled-jobs.test.ts @@ -0,0 +1,113 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + findSchedules: vi.fn(), + scheduleJob: vi.fn(), +})); + +vi.mock("@dokploy/server", () => ({ + CLEANUP_CRON_JOB: "0 0 * * *", + cleanupAll: vi.fn(), + findBackupById: vi.fn(), + findScheduleById: vi.fn(), + findServerById: vi.fn(), + findVolumeBackupById: vi.fn(), + keepLatestNBackups: vi.fn(), + runCommand: vi.fn(), + runComposeBackup: vi.fn(), + runLibsqlBackup: vi.fn(), + runMariadbBackup: vi.fn(), + runMongoBackup: vi.fn(), + runMySqlBackup: vi.fn(), + runPostgresBackup: vi.fn(), + runVolumeBackup: vi.fn(), +})); +vi.mock("@dokploy/server/db", () => ({ + and: vi.fn(), + eq: vi.fn(), + backups: {}, + schedules: {}, + server: {}, + volumeBackups: {}, + db: { + query: { + server: { findMany: vi.fn(async () => []) }, + backups: { findMany: vi.fn(async () => []) }, + schedules: { findMany: mocks.findSchedules }, + volumeBackups: { findMany: vi.fn(async () => []) }, + }, + }, +})); +vi.mock("../../../schedules/src/queue.js", () => ({ + scheduleJob: mocks.scheduleJob, +})); +vi.mock("../../../schedules/src/logger.js", () => ({ + logger: { info: vi.fn(), error: vi.fn() }, +})); + +const { initializeJobs } = await import("../../../schedules/src/utils.js"); + +const targets = [ + "application", + "compose", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", +] as const; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe.each(targets)("%s scheduled job startup", (target) => { + it.each([null, "Australia/Sydney"])( + "restores active-server jobs with timezone %s", + async (timezone) => { + mocks.findSchedules.mockResolvedValue([ + { + scheduleId: "schedule-1", + cronExpression: "0 9 * * *", + timezone, + [target]: { server: { serverStatus: "active" } }, + }, + ]); + + await initializeJobs(); + + expect(mocks.findSchedules).toHaveBeenCalledWith( + expect.objectContaining({ + with: expect.objectContaining({ + [target]: expect.objectContaining({ with: { server: true } }), + }), + }), + ); + expect(mocks.scheduleJob).toHaveBeenCalledExactlyOnceWith({ + type: "schedule", + scheduleId: "schedule-1", + cronSchedule: "0 9 * * *", + timezone, + }); + }, + ); + + it.each(["inactive", null])( + "does not restore jobs whose server is %s", + async (serverStatus) => { + mocks.findSchedules.mockResolvedValue([ + { + scheduleId: "schedule-1", + cronExpression: "0 9 * * *", + timezone: null, + [target]: { server: serverStatus ? { serverStatus } : null }, + }, + ]); + + await initializeJobs(); + + expect(mocks.scheduleJob).not.toHaveBeenCalled(); + }, + ); +}); diff --git a/apps/dokploy/__test__/schedules/schedule-lifecycle.test.ts b/apps/dokploy/__test__/schedules/schedule-lifecycle.test.ts new file mode 100644 index 00000000000..e8e51ee0be6 --- /dev/null +++ b/apps/dokploy/__test__/schedules/schedule-lifecycle.test.ts @@ -0,0 +1,165 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + findScheduleById: vi.fn(), + createSchedule: vi.fn(), + updateSchedule: vi.fn(), + checkDatabaseScheduleAccess: vi.fn(), + schedule: vi.fn(), + removeJob: vi.fn(), + updateJob: vi.fn(), + getDatabaseScheduleId: vi.fn(), + isDatabaseScheduleType: vi.fn(), +})); + +vi.mock("@trpc/server", () => ({ TRPCError: Error })); +vi.mock("@dokploy/server/db/schema/deployment", () => ({ deployments: {} })); +vi.mock("@dokploy/server/db/schema/schedule", () => ({ + createScheduleSchema: {}, + updateScheduleSchema: {}, + databaseScheduleIdColumns: { postgres: "postgresId" }, + getDatabaseScheduleId: mocks.getDatabaseScheduleId, + isDatabaseScheduleType: mocks.isDatabaseScheduleType, + isValidDatabaseScheduleTarget: () => true, + schedules: {}, + scheduleType: { enumValues: ["application", "compose", "postgres"] }, +})); +vi.mock("@dokploy/server", () => ({ + IS_CLOUD: true, + removeScheduleJob: vi.fn(), + scheduleJob: vi.fn(), + runCommand: vi.fn(), +})); +vi.mock("@dokploy/server/db", () => ({ db: {} })); +vi.mock("@dokploy/server/services/permission", () => ({ + checkPermission: vi.fn(), + checkServicePermissionAndAccess: vi.fn(), + findMemberByUserId: vi.fn(), +})); +vi.mock("@dokploy/server/services/schedule", () => ({ + assertHostScheduleAccess: vi.fn(), + createSchedule: mocks.createSchedule, + deleteSchedule: vi.fn(), + findScheduleById: mocks.findScheduleById, + updateSchedule: mocks.updateSchedule, +})); +vi.mock("@dokploy/server/services/server", () => ({ + findServerById: vi.fn(), +})); +vi.mock("@/server/api/utils/audit", () => ({ audit: vi.fn() })); +vi.mock("@/server/api/utils/database-schedule-access", () => ({ + checkDatabaseScheduleAccess: mocks.checkDatabaseScheduleAccess, +})); +vi.mock("@/server/api/utils/plan-limits", () => ({ + assertScheduledJobLimit: vi.fn(), +})); +vi.mock("@/server/utils/backup", () => ({ + schedule: mocks.schedule, + removeJob: mocks.removeJob, + updateJob: mocks.updateJob, +})); +vi.mock("@/server/api/trpc", () => ({ + createTRPCRouter: (routes: unknown) => routes, + protectedProcedure: { + input: () => ({ + query: (handler: unknown) => handler, + mutation: (handler: unknown) => handler, + }), + }, +})); + +const { scheduleRouter } = await import("@/server/api/routers/schedule"); +type Handler = (args: { + input: Record; + ctx: typeof ctx; +}) => Promise; +const create = scheduleRouter.create as unknown as Handler; +const update = scheduleRouter.update as unknown as Handler; +const ctx = { + user: { id: "user-1" }, + session: { activeOrganizationId: "org-1" }, +}; + +beforeEach(() => { + vi.resetAllMocks(); + mocks.getDatabaseScheduleId.mockReturnValue(null); + mocks.isDatabaseScheduleType.mockReturnValue(false); +}); + +describe("cloud schedule lifecycle", () => { + it.each(["application", "compose", "postgres"] as const)( + "removes the existing %s repeat key when disabling and editing together", + async (type) => { + mocks.isDatabaseScheduleType.mockReturnValue(type === "postgres"); + mocks.getDatabaseScheduleId.mockReturnValue( + type === "postgres" ? "service-1" : null, + ); + const existing = { + scheduleId: "schedule-1", + scheduleType: type, + [`${type}Id`]: "service-1", + cronExpression: "0 9 * * *", + timezone: "Australia/Sydney", + enabled: true, + }; + mocks.findScheduleById.mockResolvedValue(existing); + mocks.updateSchedule.mockResolvedValue({ + ...existing, + cronExpression: "0 10 * * *", + timezone: "Europe/London", + enabled: false, + }); + + await update({ + input: { + scheduleId: "schedule-1", + cronExpression: "0 10 * * *", + timezone: "Europe/London", + enabled: false, + }, + ctx, + }); + + expect(mocks.removeJob).toHaveBeenCalledExactlyOnceWith({ + type: "schedule", + scheduleId: "schedule-1", + cronSchedule: "0 9 * * *", + timezone: "Australia/Sydney", + }); + expect(mocks.updateJob).not.toHaveBeenCalled(); + if (type === "postgres") { + expect(mocks.checkDatabaseScheduleAccess).toHaveBeenCalledWith( + ctx, + "postgres", + "service-1", + { schedule: ["update"] }, + ); + } + }, + ); + + it("propagates queue registration failures to the create caller", async () => { + mocks.isDatabaseScheduleType.mockReturnValue(true); + mocks.getDatabaseScheduleId.mockReturnValue("database-1"); + mocks.createSchedule.mockResolvedValue({ + scheduleId: "schedule-1", + enabled: true, + cronExpression: "0 9 * * *", + timezone: null, + }); + mocks.schedule.mockRejectedValue(new Error("Queue unavailable")); + + await expect( + create({ + input: { scheduleType: "postgres", postgresId: "database-1" }, + ctx, + }), + ).rejects.toThrow("Queue unavailable"); + expect(mocks.checkDatabaseScheduleAccess).toHaveBeenCalledWith( + ctx, + "postgres", + "database-1", + { schedule: ["create"] }, + ); + }); +}); diff --git a/apps/dokploy/__test__/schedules/scheduled-deployment-cleanup.test.ts b/apps/dokploy/__test__/schedules/scheduled-deployment-cleanup.test.ts new file mode 100644 index 00000000000..3e1f92618e0 --- /dev/null +++ b/apps/dokploy/__test__/schedules/scheduled-deployment-cleanup.test.ts @@ -0,0 +1,158 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + deleteDeployment: vi.fn(), + findScheduleById: vi.fn(), + execAsync: vi.fn(), + execAsyncRemote: vi.fn(), +})); + +vi.mock("@trpc/server", () => ({ TRPCError: Error })); +vi.mock("date-fns", () => ({ format: vi.fn() })); +vi.mock("@dokploy/server/constants", () => ({ paths: vi.fn() })); +vi.mock("@dokploy/server/db", () => ({ + db: { + delete: () => ({ + where: () => ({ returning: mocks.deleteDeployment }), + }), + }, +})); +vi.mock("@dokploy/server/db/schema", () => ({ + applications: {}, + compose: {}, + deployments: {}, + environments: {}, + projects: {}, +})); +vi.mock("@dokploy/server/db/schema/schedule", () => ({ schedules: {} })); +vi.mock("@dokploy/server/utils/docker/utils", () => ({ + encodeBase64: vi.fn(), +})); +vi.mock("@dokploy/server/services/permission", () => ({ + findMemberByUserId: vi.fn(), +})); +vi.mock("@dokploy/server/utils/filesystem/directory", () => ({ + removeDirectoryIfExistsContent: vi.fn(), +})); +vi.mock("@dokploy/server/utils/process/execAsync", () => ({ + execAsync: mocks.execAsync, + execAsyncRemote: mocks.execAsyncRemote, +})); +vi.mock("@dokploy/server/services/application", () => ({ + findApplicationById: vi.fn(), + updateApplicationStatus: vi.fn(), +})); +vi.mock("@dokploy/server/services/backup", () => ({ + findBackupById: vi.fn(), +})); +vi.mock("@dokploy/server/services/compose", () => ({ + findComposeById: vi.fn(), + updateCompose: vi.fn(), +})); +vi.mock("@dokploy/server/services/preview-deployment", () => ({ + findPreviewDeploymentById: vi.fn(), + updatePreviewDeployment: vi.fn(), +})); +vi.mock("@dokploy/server/services/rollbacks", () => ({ + removeRollbackById: vi.fn(), +})); +vi.mock("@dokploy/server/services/schedule", async (importOriginal) => ({ + ...(await importOriginal< + typeof import("@dokploy/server/services/schedule") + >()), + findScheduleById: mocks.findScheduleById, +})); +vi.mock("@dokploy/server/services/server", () => ({ + findServerById: vi.fn(), +})); +vi.mock("@dokploy/server/services/volume-backups", () => ({ + findVolumeBackupById: vi.fn(), +})); + +const { removeDeployment } = await import( + "@dokploy/server/services/deployment" +); + +beforeEach(() => { + vi.resetAllMocks(); + mocks.deleteDeployment.mockResolvedValue([ + { + deploymentId: "run-1", + scheduleId: "schedule-1", + serverId: null, + logPath: "/logs/schedule/run.log", + }, + ]); +}); + +describe("scheduled deployment cleanup", () => { + it.each([ + "application", + "compose", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", + ] as const)( + "removes remote %s schedule logs on the service server", + async (type) => { + const schedule = { + scheduleId: "schedule-1", + [type]: { serverId: "service-server" }, + }; + mocks.findScheduleById.mockResolvedValue(schedule); + + await removeDeployment("run-1"); + + expect(mocks.findScheduleById).toHaveBeenCalledWith("schedule-1"); + expect(mocks.execAsyncRemote).toHaveBeenCalledExactlyOnceWith( + "service-server", + "rm -f /logs/schedule/run.log;", + ); + expect(mocks.execAsync).not.toHaveBeenCalled(); + }, + ); + + it("keeps local schedule log cleanup local", async () => { + mocks.findScheduleById.mockResolvedValue({ scheduleId: "schedule-1" }); + + await removeDeployment("run-1"); + + expect(mocks.execAsync).toHaveBeenCalledExactlyOnceWith( + "rm -f /logs/schedule/run.log;", + ); + expect(mocks.execAsyncRemote).not.toHaveBeenCalled(); + }); + + it("keeps explicit deployment server routing", async () => { + mocks.deleteDeployment.mockResolvedValue([ + { + deploymentId: "run-1", + serverId: "deployment-server", + logPath: "/logs/deployment.log", + }, + ]); + + await removeDeployment("run-1"); + + expect(mocks.findScheduleById).not.toHaveBeenCalled(); + expect(mocks.execAsyncRemote).toHaveBeenCalledExactlyOnceWith( + "deployment-server", + "rm -f /logs/deployment.log;", + ); + }); + + it("does not execute cleanup for a missing log path", async () => { + mocks.deleteDeployment.mockResolvedValue([ + { deploymentId: "run-1", scheduleId: "schedule-1", logPath: "" }, + ]); + + await removeDeployment("run-1"); + + expect(mocks.findScheduleById).not.toHaveBeenCalled(); + expect(mocks.execAsync).not.toHaveBeenCalled(); + expect(mocks.execAsyncRemote).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/dokploy/__test__/schedules/scheduled-job-timezones.test.ts b/apps/dokploy/__test__/schedules/scheduled-job-timezones.test.ts new file mode 100644 index 00000000000..eaeba5425da --- /dev/null +++ b/apps/dokploy/__test__/schedules/scheduled-job-timezones.test.ts @@ -0,0 +1,72 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { jobQueueSchema } from "../../../schedules/src/schema"; + +const mocks = vi.hoisted(() => ({ + add: vi.fn(), + removeRepeatable: vi.fn(), +})); + +// BullMQ is a dependency of the schedules workspace, not the dokploy test workspace. +// Mock its resolved path so the queue never attempts a real Redis connection. +vi.mock("../../../schedules/node_modules/bullmq", () => ({ + Queue: class { + add = mocks.add; + removeRepeatable = mocks.removeRepeatable; + }, +})); +vi.mock("../../../schedules/src/logger.js", () => ({ logger: {} })); + +const { jobQueue, removeJob, scheduleJob } = await import( + "../../../schedules/src/queue.js" +); + +beforeEach(() => { + vi.clearAllMocks(); + expect(jobQueue.add).toBe(mocks.add); + expect(jobQueue.removeRepeatable).toBe(mocks.removeRepeatable); +}); + +describe("scheduled job timezone contract", () => { + it.each([undefined, null, "UTC", "Australia/Sydney"])( + "uses the same repeat key when creating and removing a job with timezone %s", + async (timezone) => { + const job = jobQueueSchema.parse({ + type: "schedule", + scheduleId: "schedule-1", + cronSchedule: "0 9 * * *", + timezone, + }); + const repeat = { + pattern: "0 9 * * *", + tz: timezone || "UTC", + }; + + await scheduleJob(job); + await removeJob(job); + + expect(mocks.add).toHaveBeenCalledWith("schedule-1", job, { repeat }); + expect(mocks.removeRepeatable).toHaveBeenCalledWith("schedule-1", repeat); + }, + ); + + it("accepts omitted timezones in existing schedule API requests", () => { + expect( + jobQueueSchema.safeParse({ + type: "schedule", + scheduleId: "schedule-1", + cronSchedule: "0 9 * * *", + }).success, + ).toBe(true); + }); + + it("rejects non-string timezone values", () => { + expect( + jobQueueSchema.safeParse({ + type: "schedule", + scheduleId: "schedule-1", + cronSchedule: "0 9 * * *", + timezone: 10, + }).success, + ).toBe(false); + }); +}); diff --git a/apps/dokploy/__test__/utils/remote-stream-early-exit.test.ts b/apps/dokploy/__test__/utils/remote-stream-early-exit.test.ts new file mode 100644 index 00000000000..81d97245e09 --- /dev/null +++ b/apps/dokploy/__test__/utils/remote-stream-early-exit.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it, vi } from "vitest"; + +vi.mock("@dokploy/server/services/server", () => ({ + findServerById: vi.fn(), +})); + +const state = vi.hoisted(() => ({ + source: null as null | { + stdout: import("node:stream").PassThrough; + kill: () => boolean; + }, +})); + +vi.mock("node:child_process", async () => { + const { EventEmitter } = await import("node:events"); + const { PassThrough } = await import("node:stream"); + + return { + spawn: vi.fn((_shell: string, args: string[]) => { + const child = Object.assign(new EventEmitter(), { + stdin: new PassThrough(), + stdout: new PassThrough(), + stderr: new PassThrough(), + kill: vi.fn(() => true), + }); + + if (args[1] === "echo boom >&2; exit 3") { + child.stdin.end(); + child.stdin.emit("close"); + child.stderr.once("end", () => child.emit("close", 3)); + queueMicrotask(() => { + child.stderr.end("boom\n"); + child.stdout.end(); + }); + } else { + state.source = child; + child.stdout.once("end", () => child.emit("close", 0)); + if (args[1] !== "source never finishes") { + queueMicrotask(() => { + child.stdout.end("x"); + child.stderr.end(); + }); + } + } + return child; + }), + }; +}); + +import { pipeBetweenServers } from "@dokploy/server/utils/process/remoteStream"; + +describe("pipeBetweenServers early target failure", () => { + it("drains the source and rejects if target stdin closed before piping", async () => { + await expect( + pipeBetweenServers({ + source: { serverId: null, command: "printf x" }, + target: { serverId: null, command: "echo boom >&2; exit 3" }, + }), + ).rejects.toThrow("target exited with code 3: boom"); + + await vi.waitFor(() => { + expect(state.source?.stdout.readableEnded).toBe(true); + }); + expect(state.source?.kill).toHaveBeenCalledOnce(); + }, 1_000); + it("rejects promptly even if the failed target leaves the source open", async () => { + await expect( + pipeBetweenServers({ + source: { serverId: null, command: "source never finishes" }, + target: { serverId: null, command: "echo boom >&2; exit 3" }, + }), + ).rejects.toThrow("target exited with code 3: boom"); + + expect(state.source?.kill).toHaveBeenCalledOnce(); + }, 1_000); +}); diff --git a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx index 6d3210a8d8a..0fd9482a107 100644 --- a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx @@ -66,6 +66,25 @@ import { api } from "@/utils/api"; import type { CacheType } from "../domains/handle-domain"; import { getTimezoneLabel, TIMEZONES } from "./timezones"; +export const databaseScheduleTypes = [ + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", +] as const; + +export type ScheduleTargetType = + | "application" + | "compose" + | "server" + | "dokploy-server" + | (typeof databaseScheduleTypes)[number]; + +export const isDatabaseSchedule = (type: ScheduleTargetType) => + databaseScheduleTypes.some((databaseType) => databaseType === type); + export const commonCronExpressions = [ { label: "Every minute", value: "* * * * *" }, { label: "Every hour", value: "0 * * * *" }, @@ -91,6 +110,12 @@ const formSchema = z "compose", "server", "dokploy-server", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", ]), script: z.string(), timezone: z.string().optional(), @@ -118,7 +143,8 @@ const formSchema = z if ( (data.scheduleType === "application" || - data.scheduleType === "compose") && + data.scheduleType === "compose" || + isDatabaseSchedule(data.scheduleType)) && !data.command ) { ctx.addIssue({ @@ -132,7 +158,7 @@ const formSchema = z interface Props { id?: string; scheduleId?: string; - scheduleType?: "application" | "compose" | "server" | "dokploy-server"; + scheduleType?: ScheduleTargetType; } export const ScheduleFormField = ({ @@ -227,7 +253,8 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { name: "", description: "", cronExpression: "", - shellType: "bash", + shellType: + scheduleType && isDatabaseSchedule(scheduleType) ? "sh" : "bash", command: "", enabled: true, serviceName: "", @@ -294,6 +321,13 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { ...(scheduleType === "compose" && { composeId: id || "", }), + ...(scheduleType === "postgres" && { postgresId: id || "" }), + ...(scheduleType === "mysql" && { mysqlId: id || "" }), + ...(scheduleType === "mariadb" && { mariadbId: id || "" }), + ...(scheduleType === "mongo" && { mongoId: id || "" }), + ...(scheduleType === "redis" && { redisId: id || "" }), + ...(scheduleType === "libsql" && { libsqlId: id || "" }), + ...(scheduleType === "server" && { serverId: id || "", }), @@ -588,7 +622,8 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { /> {(scheduleTypeForm === "application" || - scheduleTypeForm === "compose") && ( + scheduleTypeForm === "compose" || + isDatabaseSchedule(scheduleTypeForm)) && ( <> { Command - + The command to execute in your container diff --git a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx index f6fb952ce66..a0b97784d3c 100644 --- a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx @@ -26,11 +26,11 @@ import { } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { ShowDeploymentsModal } from "../deployments/show-deployments-modal"; -import { HandleSchedules } from "./handle-schedules"; +import { HandleSchedules, type ScheduleTargetType } from "./handle-schedules"; interface Props { id: string; - scheduleType?: "application" | "compose" | "server" | "dokploy-server"; + scheduleType?: ScheduleTargetType; } export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => { @@ -103,7 +103,14 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => { const serverId = schedule.serverId || schedule.application?.serverId || - schedule.compose?.serverId; + schedule.compose?.serverId || + schedule.postgres?.serverId || + schedule.mysql?.serverId || + schedule.mariadb?.serverId || + schedule.mongo?.serverId || + schedule.redis?.serverId || + schedule.libsql?.serverId || + null; return (
statement-breakpoint +ALTER TYPE "public"."scheduleType" ADD VALUE IF NOT EXISTS 'mysql';--> statement-breakpoint +ALTER TYPE "public"."scheduleType" ADD VALUE IF NOT EXISTS 'mariadb';--> statement-breakpoint +ALTER TYPE "public"."scheduleType" ADD VALUE IF NOT EXISTS 'mongo';--> statement-breakpoint +ALTER TYPE "public"."scheduleType" ADD VALUE IF NOT EXISTS 'redis';--> statement-breakpoint +ALTER TYPE "public"."scheduleType" ADD VALUE IF NOT EXISTS 'libsql';--> statement-breakpoint +ALTER TABLE "schedule" ADD COLUMN IF NOT EXISTS "postgresId" text;--> statement-breakpoint +ALTER TABLE "schedule" ADD COLUMN IF NOT EXISTS "mysqlId" text;--> statement-breakpoint +ALTER TABLE "schedule" ADD COLUMN IF NOT EXISTS "mariadbId" text;--> statement-breakpoint +ALTER TABLE "schedule" ADD COLUMN IF NOT EXISTS "mongoId" text;--> statement-breakpoint +ALTER TABLE "schedule" ADD COLUMN IF NOT EXISTS "redisId" text;--> statement-breakpoint +ALTER TABLE "schedule" ADD COLUMN IF NOT EXISTS "libsqlId" text;--> statement-breakpoint +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = 'public.schedule'::regclass + AND conname = 'schedule_postgresId_postgres_postgresId_fk' + ) THEN + ALTER TABLE "schedule" ADD CONSTRAINT "schedule_postgresId_postgres_postgresId_fk" FOREIGN KEY ("postgresId") REFERENCES "public"."postgres"("postgresId") ON DELETE cascade ON UPDATE no action; + END IF; +END +$$;--> statement-breakpoint +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = 'public.schedule'::regclass + AND conname = 'schedule_mysqlId_mysql_mysqlId_fk' + ) THEN + ALTER TABLE "schedule" ADD CONSTRAINT "schedule_mysqlId_mysql_mysqlId_fk" FOREIGN KEY ("mysqlId") REFERENCES "public"."mysql"("mysqlId") ON DELETE cascade ON UPDATE no action; + END IF; +END +$$;--> statement-breakpoint +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = 'public.schedule'::regclass + AND conname = 'schedule_mariadbId_mariadb_mariadbId_fk' + ) THEN + ALTER TABLE "schedule" ADD CONSTRAINT "schedule_mariadbId_mariadb_mariadbId_fk" FOREIGN KEY ("mariadbId") REFERENCES "public"."mariadb"("mariadbId") ON DELETE cascade ON UPDATE no action; + END IF; +END +$$;--> statement-breakpoint +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = 'public.schedule'::regclass + AND conname = 'schedule_mongoId_mongo_mongoId_fk' + ) THEN + ALTER TABLE "schedule" ADD CONSTRAINT "schedule_mongoId_mongo_mongoId_fk" FOREIGN KEY ("mongoId") REFERENCES "public"."mongo"("mongoId") ON DELETE cascade ON UPDATE no action; + END IF; +END +$$;--> statement-breakpoint +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = 'public.schedule'::regclass + AND conname = 'schedule_redisId_redis_redisId_fk' + ) THEN + ALTER TABLE "schedule" ADD CONSTRAINT "schedule_redisId_redis_redisId_fk" FOREIGN KEY ("redisId") REFERENCES "public"."redis"("redisId") ON DELETE cascade ON UPDATE no action; + END IF; +END +$$;--> statement-breakpoint +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = 'public.schedule'::regclass + AND conname = 'schedule_libsqlId_libsql_libsqlId_fk' + ) THEN + ALTER TABLE "schedule" ADD CONSTRAINT "schedule_libsqlId_libsql_libsqlId_fk" FOREIGN KEY ("libsqlId") REFERENCES "public"."libsql"("libsqlId") ON DELETE cascade ON UPDATE no action; + END IF; +END +$$; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0198_snapshot.json b/apps/dokploy/drizzle/meta/0198_snapshot.json index 0812a43a4b4..932e65448d3 100644 --- a/apps/dokploy/drizzle/meta/0198_snapshot.json +++ b/apps/dokploy/drizzle/meta/0198_snapshot.json @@ -9317,4 +9317,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/apps/dokploy/drizzle/meta/0199_snapshot.json b/apps/dokploy/drizzle/meta/0199_snapshot.json new file mode 100644 index 00000000000..14e0eeb9cde --- /dev/null +++ b/apps/dokploy/drizzle/meta/0199_snapshot.json @@ -0,0 +1,9440 @@ +{ + "id": "3b27cb44-a99d-4de8-87dc-30dbeb6d2bc4", + "prevId": "4c5cd64c-f0fa-4b62-9a8e-504f2f435212", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_reference_id_user_id_fk": { + "name": "apikey_reference_id_user_id_fk", + "tableFrom": "apikey", + "tableTo": "user", + "columnsFrom": [ + "reference_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteEnvironments": { + "name": "canDeleteEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateEnvironments": { + "name": "canCreateEnvironments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accessedEnvironments": { + "name": "accessedEnvironments", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accessedGitProviders": { + "name": "accessedGitProviders", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accessedServers": { + "name": "accessedServers", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_role": { + "name": "default_role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_id_fk": { + "name": "organization_owner_id_user_id_fk", + "tableFrom": "organization", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_role": { + "name": "organization_role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "organizationRole_organizationId_idx": { + "name": "organizationRole_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "organizationRole_role_idx": { + "name": "organizationRole_role_idx", + "columns": [ + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_role_organization_id_organization_id_fk": { + "name": "organization_role_organization_id_organization_id_fk", + "tableFrom": "organization_role", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.passkey": { + "name": "passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backed_up": { + "name": "backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "passkey_userId_idx": { + "name": "passkey_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "passkey_credentialID_idx": { + "name": "passkey_credentialID_idx", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "passkey_user_id_user_id_fk": { + "name": "passkey_user_id_user_id_fk", + "tableFrom": "passkey", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "failed_verification_count": { + "name": "failed_verification_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_id_fk": { + "name": "two_factor_user_id_user_id_fk", + "tableFrom": "two_factor", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai": { + "name": "ai", + "schema": "", + "columns": { + "aiId": { + "name": "aiId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiUrl": { + "name": "apiUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isEnabled": { + "name": "isEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ai_organizationId_organization_id_fk": { + "name": "ai_organizationId_organization_id_fk", + "tableFrom": "ai", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildSecrets": { + "name": "previewBuildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLabels": { + "name": "previewLabels", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewCustomCertResolver": { + "name": "previewCustomCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewRequireCollaboratorPermissions": { + "name": "previewRequireCollaboratorPermissions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "rollbackActive": { + "name": "rollbackActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildSecrets": { + "name": "buildSecrets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "cleanCache": { + "name": "cleanCache", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBuildPath": { + "name": "giteaBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepositorySlug": { + "name": "bitbucketRepositorySlug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Dockerfile'" + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "ulimitsSwarm": { + "name": "ulimitsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "railpackVersion": { + "name": "railpackVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0.15.4'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isStaticSpa": { + "name": "isStaticSpa", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "createEnvFile": { + "name": "createEnvFile", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackRegistryId": { + "name": "rollbackRegistryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildServerId": { + "name": "buildServerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildRegistryId": { + "name": "buildRegistryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "networkIds": { + "name": "networkIds", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "detachDokployNetwork": { + "name": "detachDokployNetwork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_rollbackRegistryId_registry_registryId_fk": { + "name": "application_rollbackRegistryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "rollbackRegistryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_environmentId_environment_environmentId_fk": { + "name": "application_environmentId_environment_environmentId_fk", + "tableFrom": "application", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_giteaId_gitea_giteaId_fk": { + "name": "application_giteaId_gitea_giteaId_fk", + "tableFrom": "application", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_buildServerId_server_serverId_fk": { + "name": "application_buildServerId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "buildServerId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_buildRegistryId_registry_registryId_fk": { + "name": "application_buildRegistryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "buildRegistryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_log": { + "name": "audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_role": { + "name": "user_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_name": { + "name": "resource_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auditLog_organizationId_idx": { + "name": "auditLog_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auditLog_userId_idx": { + "name": "auditLog_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auditLog_createdAt_idx": { + "name": "auditLog_createdAt_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_log_organization_id_organization_id_fk": { + "name": "audit_log_organization_id_organization_id_fk", + "tableFrom": "audit_log", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "audit_log_user_id_user_id_fk": { + "name": "audit_log_user_id_user_id_fk", + "tableFrom": "audit_log", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customName": { + "name": "customName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "includeEncryptionKey": { + "name": "includeEncryptionKey", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "backupType": { + "name": "backupType", + "type": "backupType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'database'" + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "libsqlId": { + "name": "libsqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_composeId_compose_composeId_fk": { + "name": "backup_composeId_compose_composeId_fk", + "tableFrom": "backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_libsqlId_libsql_libsqlId_fk": { + "name": "backup_libsqlId_libsql_libsqlId_fk", + "tableFrom": "backup", + "tableTo": "libsql", + "columnsFrom": [ + "libsqlId" + ], + "columnsTo": [ + "libsqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_userId_user_id_fk": { + "name": "backup_userId_user_id_fk", + "tableFrom": "backup", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "backup_appName_unique": { + "name": "backup_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketEmail": { + "name": "bitbucketEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "apiToken": { + "name": "apiToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepositorySlug": { + "name": "bitbucketRepositorySlug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createEnvFile": { + "name": "createEnvFile", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeploymentsVolume": { + "name": "isolatedDeploymentsVolume", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pullImages": { + "name": "pullImages", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceNetworks": { + "name": "serviceNetworks", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_environmentId_environment_environmentId_fk": { + "name": "compose_environmentId_environment_environmentId_fk", + "tableFrom": "compose", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_giteaId_gitea_giteaId_fk": { + "name": "compose_giteaId_gitea_giteaId_fk", + "tableFrom": "compose", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pid": { + "name": "pid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startedAt": { + "name": "startedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finishedAt": { + "name": "finishedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildServerId": { + "name": "buildServerId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_scheduleId_schedule_scheduleId_fk": { + "name": "deployment_scheduleId_schedule_scheduleId_fk", + "tableFrom": "deployment", + "tableTo": "schedule", + "columnsFrom": [ + "scheduleId" + ], + "columnsTo": [ + "scheduleId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_backupId_backup_backupId_fk": { + "name": "deployment_backupId_backup_backupId_fk", + "tableFrom": "deployment", + "tableTo": "backup", + "columnsFrom": [ + "backupId" + ], + "columnsTo": [ + "backupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_rollbackId_rollback_rollbackId_fk": { + "name": "deployment_rollbackId_rollback_rollbackId_fk", + "tableFrom": "deployment", + "tableTo": "rollback", + "columnsFrom": [ + "rollbackId" + ], + "columnsTo": [ + "rollbackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_volumeBackupId_volume_backup_volumeBackupId_fk": { + "name": "deployment_volumeBackupId_volume_backup_volumeBackupId_fk", + "tableFrom": "deployment", + "tableTo": "volume_backup", + "columnsFrom": [ + "volumeBackupId" + ], + "columnsTo": [ + "volumeBackupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_buildServerId_server_serverId_fk": { + "name": "deployment_buildServerId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "buildServerId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "additionalFlags": { + "name": "additionalFlags", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dns_provider": { + "name": "dns_provider", + "schema": "", + "columns": { + "dnsProviderId": { + "name": "dnsProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "DnsProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "dns_provider_org_name_idx": { + "name": "dns_provider_org_name_idx", + "columns": [ + { + "expression": "organizationId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dns_provider_organizationId_organization_id_fk": { + "name": "dns_provider_organizationId_organization_id_fk", + "tableFrom": "dns_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "customEntrypoint": { + "name": "customEntrypoint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customCertResolver": { + "name": "customCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "internalPath": { + "name": "internalPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "stripPath": { + "name": "stripPath", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "middlewares": { + "name": "middlewares", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "ARRAY[]::text[]" + }, + "forwardAuthEnabled": { + "name": "forwardAuthEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isDefault": { + "name": "isDefault", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "environment_projectId_project_projectId_fk": { + "name": "environment_projectId_project_projectId_fk", + "tableFrom": "environment", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forward_auth_settings": { + "name": "forward_auth_settings", + "schema": "", + "columns": { + "forwardAuthSettingsId": { + "name": "forwardAuthSettingsId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "authDomain": { + "name": "authDomain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "baseDomain": { + "name": "baseDomain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'letsencrypt'" + }, + "customCertResolver": { + "name": "customCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "providerId": { + "name": "providerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "forward_auth_settings_providerId_sso_provider_provider_id_fk": { + "name": "forward_auth_settings_providerId_sso_provider_provider_id_fk", + "tableFrom": "forward_auth_settings", + "tableTo": "sso_provider", + "columnsFrom": [ + "providerId" + ], + "columnsTo": [ + "provider_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "forward_auth_settings_serverId_server_serverId_fk": { + "name": "forward_auth_settings_serverId_server_serverId_fk", + "tableFrom": "forward_auth_settings", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "forward_auth_settings_serverId_unique": { + "name": "forward_auth_settings_serverId_unique", + "nullsNotDistinct": false, + "columns": [ + "serverId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sharedWithOrganization": { + "name": "sharedWithOrganization", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "git_provider_userId_user_id_fk": { + "name": "git_provider_userId_user_id_fk", + "tableFrom": "git_provider", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitea": { + "name": "gitea", + "schema": "", + "columns": { + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "giteaUrl": { + "name": "giteaUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitea.com'" + }, + "giteaInternalUrl": { + "name": "giteaInternalUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'repo,repo:status,read:user,read:org'" + }, + "last_authenticated_at": { + "name": "last_authenticated_at", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "gitea_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitea_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitea", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubUrl": { + "name": "githubUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://github.com'" + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "gitlabInternalUrl": { + "name": "gitlabInternalUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.libsql": { + "name": "libsql", + "schema": "", + "columns": { + "libsqlId": { + "name": "libsqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sqldNode": { + "name": "sqldNode", + "type": "sqldNode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'primary'" + }, + "sqldPrimaryUrl": { + "name": "sqldPrimaryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableNamespaces": { + "name": "enableNamespaces", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "externalGRPCPort": { + "name": "externalGRPCPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "externalAdminPort": { + "name": "externalAdminPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "networkIds": { + "name": "networkIds", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "detachDokployNetwork": { + "name": "detachDokployNetwork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "libsql_environmentId_environment_environmentId_fk": { + "name": "libsql_environmentId_environment_environmentId_fk", + "tableFrom": "libsql", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "libsql_serverId_server_serverId_fk": { + "name": "libsql_serverId_server_serverId_fk", + "tableFrom": "libsql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "libsql_appName_unique": { + "name": "libsql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "ulimitsSwarm": { + "name": "ulimitsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "networkIds": { + "name": "networkIds", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "detachDokployNetwork": { + "name": "detachDokployNetwork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_environmentId_environment_environmentId_fk": { + "name": "mariadb_environmentId_environment_environmentId_fk", + "tableFrom": "mariadb", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'mongo:8'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "ulimitsSwarm": { + "name": "ulimitsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "networkIds": { + "name": "networkIds", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "detachDokployNetwork": { + "name": "detachDokployNetwork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_environmentId_environment_environmentId_fk": { + "name": "mongo_environmentId_environment_environmentId_fk", + "tableFrom": "mongo", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "libsqlId": { + "name": "libsqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_libsqlId_libsql_libsqlId_fk": { + "name": "mount_libsqlId_libsql_libsqlId_fk", + "tableFrom": "mount", + "tableTo": "libsql", + "columnsFrom": [ + "libsqlId" + ], + "columnsTo": [ + "libsqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "ulimitsSwarm": { + "name": "ulimitsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "networkIds": { + "name": "networkIds", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "detachDokployNetwork": { + "name": "detachDokployNetwork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_environmentId_environment_environmentId_fk": { + "name": "mysql_environmentId_environment_environmentId_fk", + "tableFrom": "mysql", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.network": { + "name": "network", + "schema": "", + "columns": { + "networkId": { + "name": "networkId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerId": { + "name": "dockerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "driver": { + "name": "driver", + "type": "networkDriver", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'bridge'" + }, + "internal": { + "name": "internal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "attachable": { + "name": "attachable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableIPv4": { + "name": "enableIPv4", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enableIPv6": { + "name": "enableIPv6", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "mtu": { + "name": "mtu", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ipam": { + "name": "ipam", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "network_organizationId_organization_id_fk": { + "name": "network_organizationId_organization_id_fk", + "tableFrom": "network", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "network_serverId_server_serverId_fk": { + "name": "network_serverId_server_serverId_fk", + "tableFrom": "network", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom": { + "name": "custom", + "schema": "", + "columns": { + "customId": { + "name": "customId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lark": { + "name": "lark", + "schema": "", + "columns": { + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mattermost": { + "name": "mattermost", + "schema": "", + "columns": { + "mattermostId": { + "name": "mattermostId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "volumeBackup": { + "name": "volumeBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployBackup": { + "name": "dokployBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resendId": { + "name": "resendId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mattermostId": { + "name": "mattermostId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customId": { + "name": "customId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "larkId": { + "name": "larkId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pushoverId": { + "name": "pushoverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "teamsId": { + "name": "teamsId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_resendId_resend_resendId_fk": { + "name": "notification_resendId_resend_resendId_fk", + "tableFrom": "notification", + "tableTo": "resend", + "columnsFrom": [ + "resendId" + ], + "columnsTo": [ + "resendId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_ntfyId_ntfy_ntfyId_fk": { + "name": "notification_ntfyId_ntfy_ntfyId_fk", + "tableFrom": "notification", + "tableTo": "ntfy", + "columnsFrom": [ + "ntfyId" + ], + "columnsTo": [ + "ntfyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_mattermostId_mattermost_mattermostId_fk": { + "name": "notification_mattermostId_mattermost_mattermostId_fk", + "tableFrom": "notification", + "tableTo": "mattermost", + "columnsFrom": [ + "mattermostId" + ], + "columnsTo": [ + "mattermostId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_customId_custom_customId_fk": { + "name": "notification_customId_custom_customId_fk", + "tableFrom": "notification", + "tableTo": "custom", + "columnsFrom": [ + "customId" + ], + "columnsTo": [ + "customId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_larkId_lark_larkId_fk": { + "name": "notification_larkId_lark_larkId_fk", + "tableFrom": "notification", + "tableTo": "lark", + "columnsFrom": [ + "larkId" + ], + "columnsTo": [ + "larkId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_pushoverId_pushover_pushoverId_fk": { + "name": "notification_pushoverId_pushover_pushoverId_fk", + "tableFrom": "notification", + "tableTo": "pushover", + "columnsFrom": [ + "pushoverId" + ], + "columnsTo": [ + "pushoverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_teamsId_teams_teamsId_fk": { + "name": "notification_teamsId_teams_teamsId_fk", + "tableFrom": "notification", + "tableTo": "teams", + "columnsFrom": [ + "teamsId" + ], + "columnsTo": [ + "teamsId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ntfy": { + "name": "ntfy", + "schema": "", + "columns": { + "ntfyId": { + "name": "ntfyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "accessToken": { + "name": "accessToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pushover": { + "name": "pushover", + "schema": "", + "columns": { + "pushoverId": { + "name": "pushoverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userKey": { + "name": "userKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiToken": { + "name": "apiToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "retry": { + "name": "retry", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expire": { + "name": "expire", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.resend": { + "name": "resend", + "schema": "", + "columns": { + "resendId": { + "name": "resendId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "teamsId": { + "name": "teamsId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "messageThreadId": { + "name": "messageThreadId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.patch": { + "name": "patch", + "schema": "", + "columns": { + "patchId": { + "name": "patchId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "patchType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'update'" + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updatedAt": { + "name": "updatedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "patch_applicationId_application_applicationId_fk": { + "name": "patch_applicationId_application_applicationId_fk", + "tableFrom": "patch", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "patch_composeId_compose_composeId_fk": { + "name": "patch_composeId_compose_composeId_fk", + "tableFrom": "patch", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "patch_filepath_application_unique": { + "name": "patch_filepath_application_unique", + "nullsNotDistinct": false, + "columns": [ + "filePath", + "applicationId" + ] + }, + "patch_filepath_compose_unique": { + "name": "patch_filepath_compose_unique", + "nullsNotDistinct": false, + "columns": [ + "filePath", + "composeId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publishMode": { + "name": "publishMode", + "type": "publishModeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'host'" + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "ulimitsSwarm": { + "name": "ulimitsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "networkIds": { + "name": "networkIds", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "detachDokployNetwork": { + "name": "detachDokployNetwork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_environmentId_environment_environmentId_fk": { + "name": "postgres_environmentId_environment_environmentId_fk", + "tableFrom": "postgres", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "stopGracePeriodSwarm": { + "name": "stopGracePeriodSwarm", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "endpointSpecSwarm": { + "name": "endpointSpecSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "ulimitsSwarm": { + "name": "ulimitsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "environmentId": { + "name": "environmentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "networkIds": { + "name": "networkIds", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "detachDokployNetwork": { + "name": "detachDokployNetwork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_environmentId_environment_environmentId_fk": { + "name": "redis_environmentId_environment_environmentId_fk", + "tableFrom": "redis", + "tableTo": "environment", + "columnsFrom": [ + "environmentId" + ], + "columnsTo": [ + "environmentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rollback": { + "name": "rollback", + "schema": "", + "columns": { + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fullContext": { + "name": "fullContext", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rollback_deploymentId_deployment_deploymentId_fk": { + "name": "rollback_deploymentId_deployment_deploymentId_fk", + "tableFrom": "rollback", + "tableTo": "deployment", + "columnsFrom": [ + "deploymentId" + ], + "columnsTo": [ + "deploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.schedule": { + "name": "schedule", + "schema": "", + "columns": { + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shellType": { + "name": "shellType", + "type": "shellType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'bash'" + }, + "scheduleType": { + "name": "scheduleType", + "type": "scheduleType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "script": { + "name": "script", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "libsqlId": { + "name": "libsqlId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_applicationId_application_applicationId_fk": { + "name": "schedule_applicationId_application_applicationId_fk", + "tableFrom": "schedule", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_composeId_compose_composeId_fk": { + "name": "schedule_composeId_compose_composeId_fk", + "tableFrom": "schedule", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_serverId_server_serverId_fk": { + "name": "schedule_serverId_server_serverId_fk", + "tableFrom": "schedule", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_organizationId_organization_id_fk": { + "name": "schedule_organizationId_organization_id_fk", + "tableFrom": "schedule", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_postgresId_postgres_postgresId_fk": { + "name": "schedule_postgresId_postgres_postgresId_fk", + "tableFrom": "schedule", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_mysqlId_mysql_mysqlId_fk": { + "name": "schedule_mysqlId_mysql_mysqlId_fk", + "tableFrom": "schedule", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_mariadbId_mariadb_mariadbId_fk": { + "name": "schedule_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "schedule", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_mongoId_mongo_mongoId_fk": { + "name": "schedule_mongoId_mongo_mongoId_fk", + "tableFrom": "schedule", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_redisId_redis_redisId_fk": { + "name": "schedule_redisId_redis_redisId_fk", + "tableFrom": "schedule", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_libsqlId_libsql_libsqlId_fk": { + "name": "schedule_libsqlId_libsql_libsqlId_fk", + "tableFrom": "schedule", + "tableTo": "libsql", + "columnsFrom": [ + "libsqlId" + ], + "columnsTo": [ + "libsqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_provider": { + "name": "scim_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scim_token": { + "name": "scim_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "scim_provider_organization_id_organization_id_fk": { + "name": "scim_provider_organization_id_organization_id_fk", + "tableFrom": "scim_provider", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "scim_provider_provider_id_unique": { + "name": "scim_provider_provider_id_unique", + "nullsNotDistinct": false, + "columns": [ + "provider_id" + ] + }, + "scim_provider_scim_token_unique": { + "name": "scim_provider_scim_token_unique", + "nullsNotDistinct": false, + "columns": [ + "scim_token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "telemetryProviderIds": { + "name": "telemetryProviderIds", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "buildsConcurrency": { + "name": "buildsConcurrency", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "serverType": { + "name": "serverType", + "type": "serverType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'deploy'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sso_provider": { + "name": "sso_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "saml_config": { + "name": "saml_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domain_verified": { + "name": "domain_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sso_provider_user_id_user_id_fk": { + "name": "sso_provider_user_id_user_id_fk", + "tableFrom": "sso_provider", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "sso_provider_organization_id_organization_id_fk": { + "name": "sso_provider_organization_id_organization_id_fk", + "tableFrom": "sso_provider", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sso_provider_provider_id_unique": { + "name": "sso_provider_provider_id_unique", + "nullsNotDistinct": false, + "columns": [ + "provider_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_tag": { + "name": "project_tag", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tagId": { + "name": "tagId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "project_tag_projectId_project_projectId_fk": { + "name": "project_tag_projectId_project_projectId_fk", + "tableFrom": "project_tag", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_tag_tagId_tag_tagId_fk": { + "name": "project_tag_tagId_tag_tagId_fk", + "tableFrom": "project_tag", + "tableTo": "tag", + "columnsFrom": [ + "tagId" + ], + "columnsTo": [ + "tagId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_project_tag": { + "name": "unique_project_tag", + "nullsNotDistinct": false, + "columns": [ + "projectId", + "tagId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tag": { + "name": "tag", + "schema": "", + "columns": { + "tagId": { + "name": "tagId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "tag_organizationId_organization_id_fk": { + "name": "tag_organizationId_organization_id_fk", + "tableFrom": "tag", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_org_tag_name": { + "name": "unique_org_tag_name", + "nullsNotDistinct": false, + "columns": [ + "organizationId", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telemetryProvider": { + "name": "telemetryProvider", + "schema": "", + "columns": { + "telemetryProviderId": { + "name": "telemetryProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "TelemetryProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "signals": { + "name": "signals", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "apiSecret": { + "name": "apiSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "extraConfig": { + "name": "extraConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "telemetryProvider_organizationId_organization_id_fk": { + "name": "telemetryProvider_organizationId_organization_id_fk", + "tableFrom": "telemetryProvider", + "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 + }, + "enableEnterpriseFeatures": { + "name": "enableEnterpriseFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "licenseKey": { + "name": "licenseKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isValidEnterpriseLicense": { + "name": "isValidEnterpriseLicense", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sendInvoiceNotifications": { + "name": "sendInvoiceNotifications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isEnterpriseCloud": { + "name": "isEnterpriseCloud", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "trustedOrigins": { + "name": "trustedOrigins", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "bookmarkedTemplates": { + "name": "bookmarkedTemplates", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "ARRAY[]::text[]" + }, + "onboardingCompletedAt": { + "name": "onboardingCompletedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vault_provider": { + "name": "vault_provider", + "schema": "", + "columns": { + "vaultProviderId": { + "name": "vaultProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "VaultProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "assignments": { + "name": "assignments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "vault_provider_org_name_idx": { + "name": "vault_provider_org_name_idx", + "columns": [ + { + "expression": "organizationId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "vault_provider_organizationId_organization_id_fk": { + "name": "vault_provider_organizationId_organization_id_fk", + "tableFrom": "vault_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.volume_backup": { + "name": "volume_backup", + "schema": "", + "columns": { + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "turnOff": { + "name": "turnOff", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "libsqlId": { + "name": "libsqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "volume_backup_applicationId_application_applicationId_fk": { + "name": "volume_backup_applicationId_application_applicationId_fk", + "tableFrom": "volume_backup", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_postgresId_postgres_postgresId_fk": { + "name": "volume_backup_postgresId_postgres_postgresId_fk", + "tableFrom": "volume_backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mariadbId_mariadb_mariadbId_fk": { + "name": "volume_backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "volume_backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mongoId_mongo_mongoId_fk": { + "name": "volume_backup_mongoId_mongo_mongoId_fk", + "tableFrom": "volume_backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mysqlId_mysql_mysqlId_fk": { + "name": "volume_backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "volume_backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_redisId_redis_redisId_fk": { + "name": "volume_backup_redisId_redis_redisId_fk", + "tableFrom": "volume_backup", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_libsqlId_libsql_libsqlId_fk": { + "name": "volume_backup_libsqlId_libsql_libsqlId_fk", + "tableFrom": "volume_backup", + "tableTo": "libsql", + "columnsFrom": [ + "libsqlId" + ], + "columnsTo": [ + "libsqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_composeId_compose_composeId_fk": { + "name": "volume_backup_composeId_compose_composeId_fk", + "tableFrom": "volume_backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_destinationId_destination_destinationId_fk": { + "name": "volume_backup_destinationId_destination_destinationId_fk", + "tableFrom": "volume_backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webServerSettings": { + "name": "webServerSettings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "logCleanupCron": { + "name": "logCleanupCron", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0 0 * * *'" + }, + "telemetryProviderIds": { + "name": "telemetryProviderIds", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "vectorAgentOrganizationId": { + "name": "vectorAgentOrganizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "whitelabelingConfig": { + "name": "whitelabelingConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"appName\":null,\"appDescription\":null,\"logoUrl\":null,\"faviconUrl\":null,\"customCss\":null,\"loginLogoUrl\":null,\"supportUrl\":null,\"docsUrl\":null,\"errorPageTitle\":null,\"errorPageDescription\":null,\"footerText\":null,\"ogImageUrl\":null}'::jsonb" + }, + "remoteServersOnly": { + "name": "remoteServersOnly", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "buildsConcurrency": { + "name": "buildsConcurrency", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "enforceSSO": { + "name": "enforceSSO", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "webServerSettings_vectorAgentOrganizationId_organization_id_fk": { + "name": "webServerSettings_vectorAgentOrganizationId_organization_id_fk", + "tableFrom": "webServerSettings", + "tableTo": "organization", + "columnsFrom": [ + "vectorAgentOrganizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + "railpack" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "drop" + ] + }, + "public.backupType": { + "name": "backupType", + "schema": "public", + "values": [ + "database", + "compose" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo", + "web-server", + "libsql" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "raw" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error", + "cancelled" + ] + }, + "public.DnsProviderType": { + "name": "DnsProviderType", + "schema": "public", + "values": [ + "cloudflare", + "route53", + "porkbun", + "infomaniak", + "ovh" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket", + "gitea" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose", + "libsql" + ] + }, + "public.networkDriver": { + "name": "networkDriver", + "schema": "public", + "values": [ + "bridge", + "overlay" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "resend", + "gotify", + "ntfy", + "mattermost", + "pushover", + "custom", + "lark", + "teams" + ] + }, + "public.patchType": { + "name": "patchType", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.publishModeType": { + "name": "publishModeType", + "schema": "public", + "values": [ + "ingress", + "host" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.scheduleType": { + "name": "scheduleType", + "schema": "public", + "values": [ + "application", + "compose", + "server", + "dokploy-server", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql" + ] + }, + "public.shellType": { + "name": "shellType", + "schema": "public", + "values": [ + "bash", + "sh" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + }, + "public.serverType": { + "name": "serverType", + "schema": "public", + "values": [ + "deploy", + "build" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none", + "custom" + ] + }, + "public.sqldNode": { + "name": "sqldNode", + "schema": "public", + "values": [ + "primary", + "replica" + ] + }, + "public.triggerType": { + "name": "triggerType", + "schema": "public", + "values": [ + "push", + "tag" + ] + }, + "public.TelemetryProviderType": { + "name": "TelemetryProviderType", + "schema": "public", + "values": [ + "loki", + "datadog", + "betterstack", + "elasticsearch", + "splunk_hec", + "aws_cloudwatch", + "prometheus_remote_write", + "new_relic", + "influxdb" + ] + }, + "public.VaultProviderType": { + "name": "VaultProviderType", + "schema": "public", + "values": [ + "hashicorp", + "infisical", + "aws", + "aws-parameter-store", + "doppler", + "azure", + "scaleway", + "phase" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 851518f669f..51d4d038595 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -1394,6 +1394,13 @@ "when": 1792806870958, "tag": "0198_nostalgic_mongoose", "breakpoints": true + }, + { + "idx": 199, + "version": "7", + "when": 1793104920000, + "tag": "0199_database_scheduled_jobs", + "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx index dffc1bebec6..92c8a7c7601 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx @@ -14,6 +14,7 @@ import { toast } from "sonner"; import superjson from "superjson"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowExternalLibsqlCredentials } from "@/components/dashboard/libsql/general/show-external-libsql-credentials"; @@ -49,7 +50,13 @@ import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; +type TabState = + | "projects" + | "monitoring" + | "settings" + | "backups" + | "advanced" + | "schedules"; const Libsql = ( props: InferGetServerSidePropsType, @@ -62,6 +69,7 @@ const Libsql = ( const [tab, setSab] = useState(activeTab); const { data } = api.libsql.one.useQuery({ libsqlId }); const { data: auth } = api.user.get.useQuery(); + const { data: permissions } = api.user.getPermissions.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: serverIp } = api.settings.getIp.useQuery(); @@ -202,10 +210,16 @@ const Libsql = ( className={cn( "md:grid md:w-fit max-md:overflow-y-scroll justify-start", isCloud && data?.serverId - ? "md:grid-cols-6" + ? permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6" : data?.serverId - ? "md:grid-cols-5" - : "md:grid-cols-6", + ? permissions?.schedule.read + ? "md:grid-cols-6" + : "md:grid-cols-5" + : permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6", )} > General @@ -216,6 +230,9 @@ const Libsql = ( )} Backups Advanced + {permissions?.schedule.read && ( + Schedules + )}
@@ -303,6 +320,13 @@ const Libsql = ( /> + {permissions?.schedule.read && ( + +
+ +
+
+ )} )} 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 b5ad24ee077..c838a78c105 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 @@ -14,6 +14,7 @@ import { toast } from "sonner"; import superjson from "superjson"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowExternalMariadbCredentials } from "@/components/dashboard/mariadb/general/show-external-mariadb-credentials"; @@ -50,7 +51,13 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; -type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; +type TabState = + | "projects" + | "monitoring" + | "settings" + | "backups" + | "advanced" + | "schedules"; const Mariadb = ( props: InferGetServerSidePropsType, @@ -216,10 +223,16 @@ const Mariadb = ( className={cn( "md:grid md:w-fit max-md:overflow-y-scroll justify-start", isCloud && data?.serverId - ? "md:grid-cols-6" + ? permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6" : data?.serverId - ? "md:grid-cols-5" - : "md:grid-cols-6", + ? permissions?.schedule.read + ? "md:grid-cols-6" + : "md:grid-cols-5" + : permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6", )} > General @@ -241,6 +254,9 @@ const Mariadb = ( {permissions?.service.create && ( Advanced )} + {permissions?.schedule.read && ( + Schedules + )} @@ -332,6 +348,13 @@ const Mariadb = ( )} + {permissions?.schedule.read && ( + +
+ +
+
+ )} )} 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 9599b950738..7de1723813e 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 @@ -14,6 +14,7 @@ import { toast } from "sonner"; import superjson from "superjson"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowExternalMongoCredentials } from "@/components/dashboard/mongo/general/show-external-mongo-credentials"; @@ -50,7 +51,13 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; -type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; +type TabState = + | "projects" + | "monitoring" + | "settings" + | "backups" + | "advanced" + | "schedules"; const Mongo = ( props: InferGetServerSidePropsType, @@ -216,10 +223,16 @@ const Mongo = ( className={cn( "md:grid md:w-fit max-md:overflow-y-scroll justify-start", isCloud && data?.serverId - ? "md:grid-cols-6" + ? permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6" : data?.serverId - ? "md:grid-cols-5" - : "md:grid-cols-6", + ? permissions?.schedule.read + ? "md:grid-cols-6" + : "md:grid-cols-5" + : permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6", )} > General @@ -241,6 +254,9 @@ const Mongo = ( {permissions?.service.create && ( Advanced )} + {permissions?.schedule.read && ( + Schedules + )} @@ -336,6 +352,13 @@ const Mongo = ( )} + {permissions?.schedule.read && ( + +
+ +
+
+ )} )} 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 28c7b98f990..6f39cf3d352 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 @@ -14,6 +14,7 @@ import { toast } from "sonner"; import superjson from "superjson"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; @@ -50,7 +51,13 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; -type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; +type TabState = + | "projects" + | "monitoring" + | "settings" + | "backups" + | "advanced" + | "schedules"; const MySql = ( props: InferGetServerSidePropsType, @@ -216,10 +223,16 @@ const MySql = ( className={cn( "md:grid md:w-fit max-md:overflow-y-scroll justify-start ", isCloud && data?.serverId - ? "md:grid-cols-6" + ? permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6" : data?.serverId - ? "md:grid-cols-5" - : "md:grid-cols-6", + ? permissions?.schedule.read + ? "md:grid-cols-6" + : "md:grid-cols-5" + : permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6", )} > General @@ -241,6 +254,9 @@ const MySql = ( {permissions?.service.create && ( Advanced )} + {permissions?.schedule.read && ( + Schedules + )} @@ -313,6 +329,13 @@ const MySql = ( )} + {permissions?.schedule.read && ( + +
+ +
+
+ )} )} 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 41a7fc82c93..f768d365dd9 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 @@ -14,6 +14,7 @@ import { toast } from "sonner"; import superjson from "superjson"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; @@ -50,7 +51,13 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; -type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; +type TabState = + | "projects" + | "monitoring" + | "settings" + | "backups" + | "advanced" + | "schedules"; const Postgresql = ( props: InferGetServerSidePropsType, @@ -217,10 +224,16 @@ const Postgresql = ( className={cn( "md:grid md:w-fit max-md:overflow-y-scroll justify-start", isCloud && data?.serverId - ? "md:grid-cols-6" + ? permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6" : data?.serverId - ? "md:grid-cols-5" - : "md:grid-cols-6", + ? permissions?.schedule.read + ? "md:grid-cols-6" + : "md:grid-cols-5" + : permissions?.schedule.read + ? "md:grid-cols-7" + : "md:grid-cols-6", )} > General @@ -242,6 +255,9 @@ const Postgresql = ( {permissions?.service.create && ( Advanced )} + {permissions?.schedule.read && ( + Schedules + )} @@ -321,6 +337,16 @@ const Postgresql = ( )} + {permissions?.schedule.read && ( + +
+ +
+
+ )} )} 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 349015e4119..0532418892d 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 @@ -14,6 +14,7 @@ import { toast } from "sonner"; import superjson from "superjson"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-container-monitoring"; @@ -49,7 +50,12 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; -type TabState = "projects" | "monitoring" | "settings" | "advanced"; +type TabState = + | "projects" + | "monitoring" + | "settings" + | "advanced" + | "schedules"; const Redis = ( props: InferGetServerSidePropsType, @@ -215,10 +221,16 @@ const Redis = ( className={cn( "md:grid md:w-fit max-md:overflow-y-scroll justify-start", isCloud && data?.serverId - ? "md:grid-cols-5" + ? permissions?.schedule.read + ? "md:grid-cols-6" + : "md:grid-cols-5" : data?.serverId - ? "md:grid-cols-4" - : "md:grid-cols-5", + ? permissions?.schedule.read + ? "md:grid-cols-5" + : "md:grid-cols-4" + : permissions?.schedule.read + ? "md:grid-cols-6" + : "md:grid-cols-5", )} > General @@ -239,6 +251,9 @@ const Redis = ( {permissions?.service.create && ( Advanced )} + {permissions?.schedule.read && ( + Schedules + )} @@ -325,6 +340,13 @@ const Redis = ( )} + {permissions?.schedule.read && ( + +
+ +
+
+ )} )} diff --git a/apps/dokploy/server/api/routers/deployment.ts b/apps/dokploy/server/api/routers/deployment.ts index f2b47097d38..23fb5aa86f4 100644 --- a/apps/dokploy/server/api/routers/deployment.ts +++ b/apps/dokploy/server/api/routers/deployment.ts @@ -11,12 +11,17 @@ import { findScheduleById, findVolumeBackupById, getAccessibleServerIds, + getScheduleServerId, IS_CLOUD, removeDeployment, resolveServicePath, updateDeploymentStatus, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; +import { + getDatabaseScheduleId, + isDatabaseScheduleType, +} from "@dokploy/server/db/schema/schedule"; import { checkPermission, checkServicePermissionAndAccess, @@ -27,6 +32,7 @@ import { TRPCError } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; import { z } from "zod"; import { audit } from "@/server/api/utils/audit"; +import { checkDatabaseScheduleAccess } from "@/server/api/utils/database-schedule-access"; import { apiFindAllByApplication, apiFindAllByCompose, @@ -39,6 +45,21 @@ import { myQueue } from "@/server/queues/queueSetup"; import { fetchDeployApiJobs, type QueueJobRow } from "@/server/utils/deploy"; import { createTRPCRouter, protectedProcedure, withPermission } from "../trpc"; +const checkDatabaseDeploymentAccess = async ( + ctx: Parameters[0], + schedule: NonNullable< + Awaited>["schedule"] + >, + action: "read" | "cancel", +) => { + const databaseId = getDatabaseScheduleId(schedule); + if (isDatabaseScheduleType(schedule.scheduleType) && databaseId) { + await checkDatabaseScheduleAccess(ctx, schedule.scheduleType, databaseId, { + deployment: [action], + }); + } +}; + export const deploymentRouter = createTRPCRouter({ all: protectedProcedure .input(apiFindAllByApplication) @@ -134,6 +155,15 @@ export const deploymentRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { if (input.type === "schedule") { const schedule = await findScheduleById(input.id); + const databaseId = getDatabaseScheduleId(schedule); + if (isDatabaseScheduleType(schedule.scheduleType) && databaseId) { + await checkDatabaseScheduleAccess( + ctx, + schedule.scheduleType, + databaseId, + { deployment: ["read"] }, + ); + } const serviceId = schedule.applicationId || schedule.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { @@ -210,6 +240,9 @@ export const deploymentRouter = createTRPCRouter({ ) .mutation(async ({ input, ctx }) => { const deployment = await findDeploymentById(input.deploymentId); + if (deployment.schedule) { + await checkDatabaseDeploymentAccess(ctx, deployment.schedule, "cancel"); + } const serviceId = deployment.applicationId || deployment.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { @@ -255,6 +288,9 @@ export const deploymentRouter = createTRPCRouter({ ) .mutation(async ({ input, ctx }) => { const deployment = await findDeploymentById(input.deploymentId); + if (deployment.schedule) { + await checkDatabaseDeploymentAccess(ctx, deployment.schedule, "cancel"); + } const serviceId = deployment.applicationId || deployment.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { @@ -287,6 +323,9 @@ export const deploymentRouter = createTRPCRouter({ ) .query(async ({ input, ctx }) => { const deployment = await findDeploymentById(input.deploymentId); + if (deployment.schedule) { + await checkDatabaseDeploymentAccess(ctx, deployment.schedule, "read"); + } const serviceId = deployment.applicationId || deployment.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { @@ -307,11 +346,21 @@ export const deploymentRouter = createTRPCRouter({ } const command = `tail -n ${input.tail} "${deployment.logPath}" 2>/dev/null || echo ""`; + // Database schedules own a database service, not a serverId on the + // schedule or deployment record. Resolve its server for remote logs. + const databaseScheduleServerId = + deployment.schedule && + isDatabaseScheduleType(deployment.schedule.scheduleType) + ? getScheduleServerId( + await findScheduleById(deployment.schedule.scheduleId), + ) + : null; const serverId = deployment.serverId || deployment.schedule?.serverId || deployment.application?.serverId || - deployment.compose?.serverId; + deployment.compose?.serverId || + databaseScheduleServerId; if (serverId) { const { stdout } = await execAsyncRemote(serverId, command); return stdout; diff --git a/apps/dokploy/server/api/routers/libsql.ts b/apps/dokploy/server/api/routers/libsql.ts index 3fdfcf55b79..43fd1f37076 100644 --- a/apps/dokploy/server/api/routers/libsql.ts +++ b/apps/dokploy/server/api/routers/libsql.ts @@ -42,6 +42,7 @@ import { apiUpdateLibsql, libsql as libsqlTable, } from "@/server/db/schema"; +import { cancelDatabaseScheduleJobs } from "@/server/utils/database-schedules"; export const libsqlRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateLibsql) @@ -326,6 +327,8 @@ export const libsqlRouter = createTRPCRouter({ resourceId: libsql.libsqlId, resourceName: libsql.appName, }); + await cancelDatabaseScheduleJobs("libsql", input.libsqlId); + const cleanupOperations = [ async () => await removeService(libsql?.appName, libsql.serverId), async () => await removeLibsqlById(input.libsqlId), diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 98932f8af68..4e2560e4d90 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -53,6 +53,7 @@ import { projects, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; +import { cancelDatabaseScheduleJobs } from "@/server/utils/database-schedules"; export const mariadbRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateMariaDB) @@ -295,6 +296,8 @@ export const mariadbRouter = createTRPCRouter({ resourceName: mongo.appName, }); const backups = await findBackupsByDbId(input.mariadbId, "mariadb"); + await cancelDatabaseScheduleJobs("mariadb", input.mariadbId); + const cleanupOperations = [ async () => await removeService(mongo?.appName, mongo.serverId), async () => await cancelJobs(backups), diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index 42b4107856d..16abbd4d219 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -52,6 +52,7 @@ import { projects, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; +import { cancelDatabaseScheduleJobs } from "@/server/utils/database-schedules"; export const mongoRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateMongo) @@ -349,6 +350,8 @@ export const mongoRouter = createTRPCRouter({ }); const backups = await findBackupsByDbId(input.mongoId, "mongo"); + await cancelDatabaseScheduleJobs("mongo", input.mongoId); + const cleanupOperations = [ async () => await removeService(mongo?.appName, mongo.serverId), async () => await cancelJobs(backups), diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index c315ec5d452..88fcbb66e71 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -52,6 +52,7 @@ import { projects, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; +import { cancelDatabaseScheduleJobs } from "@/server/utils/database-schedules"; export const mysqlRouter = createTRPCRouter({ create: protectedProcedure @@ -345,6 +346,8 @@ export const mysqlRouter = createTRPCRouter({ resourceName: mongo.appName, }); const backups = await findBackupsByDbId(input.mysqlId, "mysql"); + await cancelDatabaseScheduleJobs("mysql", input.mysqlId); + const cleanupOperations = [ async () => await removeService(mongo?.appName, mongo.serverId), async () => await cancelJobs(backups), diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index 97e39812358..14b8dd76f21 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -53,6 +53,7 @@ import { projects, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; +import { cancelDatabaseScheduleJobs } from "@/server/utils/database-schedules"; export const postgresRouter = createTRPCRouter({ create: protectedProcedure @@ -322,6 +323,8 @@ export const postgresRouter = createTRPCRouter({ }); const backups = await findBackupsByDbId(input.postgresId, "postgres"); + await cancelDatabaseScheduleJobs("postgres", input.postgresId); + const cleanupOperations = [ async () => await removeService(postgres?.appName, postgres.serverId), async () => await cancelJobs(backups), diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 7085b3bef3d..3355c358ece 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -67,6 +67,7 @@ import { redis, } from "@/server/db/schema"; import { getBillingStatus } from "@/server/utils/billing"; +import { cancelProjectDatabaseScheduleJobs } from "@/server/utils/database-schedules"; export const projectRouter = createTRPCRouter({ create: protectedProcedure @@ -776,6 +777,7 @@ export const projectRouter = createTRPCRouter({ }); } await checkProjectAccess(ctx, "delete", input.projectId); + await cancelProjectDatabaseScheduleJobs(currentProject.environments); const deletedProject = await deleteProject(input.projectId); await audit(ctx, { diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index fb0d8301ccf..01ff4872e3b 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -50,6 +50,7 @@ import { projects, redis as redisTable, } from "@/server/db/schema"; +import { cancelDatabaseScheduleJobs } from "@/server/utils/database-schedules"; export const redisRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateRedis) @@ -337,6 +338,8 @@ export const redisRouter = createTRPCRouter({ resourceId: redis.redisId, resourceName: redis.appName, }); + await cancelDatabaseScheduleJobs("redis", input.redisId); + const cleanupOperations = [ async () => await removeService(redis?.appName, redis.serverId), async () => await removeRedisById(input.redisId), diff --git a/apps/dokploy/server/api/routers/schedule.ts b/apps/dokploy/server/api/routers/schedule.ts index aae503e5013..26262fc47d5 100644 --- a/apps/dokploy/server/api/routers/schedule.ts +++ b/apps/dokploy/server/api/routers/schedule.ts @@ -3,7 +3,12 @@ import { db } from "@dokploy/server/db"; import { deployments } from "@dokploy/server/db/schema/deployment"; import { createScheduleSchema, + databaseScheduleIdColumns, + getDatabaseScheduleId, + isDatabaseScheduleType, + isValidDatabaseScheduleTarget, schedules, + scheduleType, updateScheduleSchema, } from "@dokploy/server/db/schema/schedule"; import { runCommand } from "@dokploy/server/index"; @@ -24,16 +29,46 @@ import { TRPCError } from "@trpc/server"; import { asc, desc, eq } from "drizzle-orm"; import { z } from "zod"; import { audit } from "@/server/api/utils/audit"; +import { checkDatabaseScheduleAccess } from "@/server/api/utils/database-schedule-access"; import { assertScheduledJobLimit } from "@/server/api/utils/plan-limits"; import { removeJob, schedule, updateJob } from "@/server/utils/backup"; import { createTRPCRouter, protectedProcedure } from "../trpc"; +const databaseTargetColumns = Object.values(databaseScheduleIdColumns); + +const assertDatabaseScheduleTarget = ( + input: z.infer, +) => { + if (!isValidDatabaseScheduleTarget(input)) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + "A database schedule must target exactly one database and no other service.", + }); + } +}; + export const scheduleRouter = createTRPCRouter({ create: protectedProcedure .input(createScheduleSchema) .mutation(async ({ input, ctx }) => { + assertDatabaseScheduleTarget(input); await assertHostScheduleAccess(ctx, input.scheduleType, input.serverId); + const databaseId = getDatabaseScheduleId(input); + if (isDatabaseScheduleType(input.scheduleType) && databaseId) { + await checkDatabaseScheduleAccess(ctx, input.scheduleType, databaseId, { + schedule: ["create"], + }); + if (IS_CLOUD) { + await assertScheduledJobLimit( + ctx.session.activeOrganizationId, + input.scheduleType, + databaseId, + ); + } + } + const serviceId = input.applicationId || input.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { @@ -46,7 +81,7 @@ export const scheduleRouter = createTRPCRouter({ serviceId, ); } - } else { + } else if (!databaseId) { await checkPermission(ctx, { schedule: ["create"] }); if (IS_CLOUD && input.scheduleType === "server" && input.serverId) { @@ -89,6 +124,37 @@ export const scheduleRouter = createTRPCRouter({ .input(updateScheduleSchema) .mutation(async ({ input, ctx }) => { const existingSchedule = await findScheduleById(input.scheduleId); + if (isDatabaseScheduleType(existingSchedule.scheduleType)) { + const immutableColumns = [ + ...databaseTargetColumns, + "applicationId", + "composeId", + "serverId", + "organizationId", + ] as const; + if ( + (input.scheduleType && + input.scheduleType !== existingSchedule.scheduleType) || + immutableColumns.some( + (column) => + input[column] !== undefined && + input[column] !== existingSchedule[column], + ) + ) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "The database schedule target cannot be changed.", + }); + } + } else if ( + isDatabaseScheduleType(input.scheduleType) || + databaseTargetColumns.some((column) => input[column]) + ) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "The schedule target cannot be changed to a database.", + }); + } if ( IS_CLOUD && @@ -117,13 +183,22 @@ export const scheduleRouter = createTRPCRouter({ ); } + const databaseId = getDatabaseScheduleId(existingSchedule); + if (isDatabaseScheduleType(existingSchedule.scheduleType) && databaseId) { + await checkDatabaseScheduleAccess( + ctx, + existingSchedule.scheduleType, + databaseId, + { schedule: ["update"] }, + ); + } const serviceId = existingSchedule.applicationId || existingSchedule.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { schedule: ["update"], }); - } else { + } else if (!databaseId) { await checkPermission(ctx, { schedule: ["update"] }); } const updatedSchedule = await updateSchedule(input); @@ -138,10 +213,10 @@ export const scheduleRouter = createTRPCRouter({ }); } else { await removeJob({ - cronSchedule: updatedSchedule.cronExpression, + cronSchedule: existingSchedule.cronExpression, scheduleId: updatedSchedule.scheduleId, type: "schedule", - timezone: updatedSchedule.timezone, + timezone: existingSchedule.timezone, }); } } else { @@ -171,12 +246,21 @@ export const scheduleRouter = createTRPCRouter({ scheduleItem.serverId, ); + const databaseId = getDatabaseScheduleId(scheduleItem); + if (isDatabaseScheduleType(scheduleItem.scheduleType) && databaseId) { + await checkDatabaseScheduleAccess( + ctx, + scheduleItem.scheduleType, + databaseId, + { schedule: ["delete"] }, + ); + } const serviceId = scheduleItem.applicationId || scheduleItem.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { schedule: ["delete"], }); - } else { + } else if (!databaseId) { await checkPermission(ctx, { schedule: ["delete"] }); } await deleteSchedule(input.scheduleId); @@ -204,16 +288,15 @@ export const scheduleRouter = createTRPCRouter({ .input( z.object({ id: z.string(), - scheduleType: z.enum([ - "application", - "compose", - "server", - "dokploy-server", - ]), + scheduleType: z.enum(scheduleType.enumValues), }), ) .query(async ({ input, ctx }) => { - if ( + if (isDatabaseScheduleType(input.scheduleType)) { + await checkDatabaseScheduleAccess(ctx, input.scheduleType, input.id, { + schedule: ["read"], + }); + } else if ( input.scheduleType === "application" || input.scheduleType === "compose" ) { @@ -249,6 +332,12 @@ export const scheduleRouter = createTRPCRouter({ } } const where = { + postgres: eq(schedules.postgresId, input.id), + mysql: eq(schedules.mysqlId, input.id), + mariadb: eq(schedules.mariadbId, input.id), + mongo: eq(schedules.mongoId, input.id), + redis: eq(schedules.redisId, input.id), + libsql: eq(schedules.libsqlId, input.id), application: eq(schedules.applicationId, input.id), compose: eq(schedules.composeId, input.id), server: eq(schedules.serverId, input.id), @@ -278,6 +367,55 @@ export const scheduleRouter = createTRPCRouter({ serverId: true, }, }, + postgres: { + columns: { + postgresId: true, + appName: true, + name: true, + serverId: true, + }, + }, + mysql: { + columns: { + mysqlId: true, + appName: true, + name: true, + serverId: true, + }, + }, + mariadb: { + columns: { + mariadbId: true, + appName: true, + name: true, + serverId: true, + }, + }, + mongo: { + columns: { + mongoId: true, + appName: true, + name: true, + serverId: true, + }, + }, + redis: { + columns: { + redisId: true, + appName: true, + name: true, + serverId: true, + }, + }, + libsql: { + columns: { + libsqlId: true, + appName: true, + name: true, + serverId: true, + }, + }, + deployments: { orderBy: [desc(deployments.createdAt)], }, @@ -289,12 +427,21 @@ export const scheduleRouter = createTRPCRouter({ .input(z.object({ scheduleId: z.string() })) .query(async ({ input, ctx }) => { const schedule = await findScheduleById(input.scheduleId); + const databaseId = getDatabaseScheduleId(schedule); + if (isDatabaseScheduleType(schedule.scheduleType) && databaseId) { + await checkDatabaseScheduleAccess( + ctx, + schedule.scheduleType, + databaseId, + { schedule: ["read"] }, + ); + } const serviceId = schedule.applicationId || schedule.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { schedule: ["read"], }); - } else { + } else if (!databaseId) { await checkPermission(ctx, { schedule: ["read"] }); if (schedule.scheduleType === "server" && schedule.serverId) { @@ -322,12 +469,21 @@ export const scheduleRouter = createTRPCRouter({ scheduleItem.serverId, ); + const databaseId = getDatabaseScheduleId(scheduleItem); + if (isDatabaseScheduleType(scheduleItem.scheduleType) && databaseId) { + await checkDatabaseScheduleAccess( + ctx, + scheduleItem.scheduleType, + databaseId, + { schedule: ["create"] }, + ); + } const serviceId = scheduleItem.applicationId || scheduleItem.composeId; if (serviceId) { await checkServicePermissionAndAccess(ctx, serviceId, { schedule: ["create"], }); - } else { + } else if (!databaseId) { await checkPermission(ctx, { schedule: ["create"] }); } try { diff --git a/apps/dokploy/server/api/utils/database-schedule-access.ts b/apps/dokploy/server/api/utils/database-schedule-access.ts new file mode 100644 index 00000000000..1339433182e --- /dev/null +++ b/apps/dokploy/server/api/utils/database-schedule-access.ts @@ -0,0 +1,54 @@ +import type { DatabaseScheduleType } from "@dokploy/server/db/schema/schedule"; +import { findLibsqlById } from "@dokploy/server/services/libsql"; +import { findMariadbById } from "@dokploy/server/services/mariadb"; +import { findMongoById } from "@dokploy/server/services/mongo"; +import { findMySqlById } from "@dokploy/server/services/mysql"; +import { + checkServicePermissionAndAccess, + type PermissionCtx, +} from "@dokploy/server/services/permission"; +import { findPostgresById } from "@dokploy/server/services/postgres"; +import { findRedisById } from "@dokploy/server/services/redis"; +import { TRPCError } from "@trpc/server"; + +/** Database schedules may only be managed by members of the owning organization. */ +export const checkDatabaseScheduleAccess = async ( + ctx: PermissionCtx, + databaseType: DatabaseScheduleType, + id: string, + permissions: Parameters[2], +) => { + await checkServicePermissionAndAccess(ctx, id, permissions); + + const service = await (async () => { + switch (databaseType) { + case "postgres": + return findPostgresById(id); + case "mysql": + return findMySqlById(id); + case "mariadb": + return findMariadbById(id); + case "mongo": + return findMongoById(id); + case "redis": + return findRedisById(id); + case "libsql": + return findLibsqlById(id); + default: + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Unsupported database type.", + }); + } + })(); + + if ( + service.environment.project.organizationId !== + ctx.session.activeOrganizationId + ) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You don't have access to this database.", + }); + } +}; diff --git a/apps/dokploy/server/api/utils/plan-limits.ts b/apps/dokploy/server/api/utils/plan-limits.ts index 31223164d26..93e373e2e8a 100644 --- a/apps/dokploy/server/api/utils/plan-limits.ts +++ b/apps/dokploy/server/api/utils/plan-limits.ts @@ -118,7 +118,16 @@ export const assertDatabaseBackupLimit = async ( export const assertScheduledJobLimit = async ( organizationId: string, - scheduleType: "application" | "compose" | "server", + scheduleType: + | "application" + | "compose" + | "server" + | "postgres" + | "mysql" + | "mariadb" + | "mongo" + | "redis" + | "libsql", serviceId: string, ) => { const plan = await getCurrentPlan(organizationId); diff --git a/apps/dokploy/server/utils/database-schedules.ts b/apps/dokploy/server/utils/database-schedules.ts new file mode 100644 index 00000000000..c59245eff86 --- /dev/null +++ b/apps/dokploy/server/utils/database-schedules.ts @@ -0,0 +1,77 @@ +import { IS_CLOUD, removeScheduleJob } from "@dokploy/server"; +import { db } from "@dokploy/server/db"; +import { + type DatabaseScheduleType, + databaseScheduleIdColumns, + schedules, +} from "@dokploy/server/db/schema/schedule"; +import type { findProjectById } from "@dokploy/server/services/project"; +import { eq } from "drizzle-orm"; +import { removeJob } from "@/server/utils/backup"; + +/** Unregister repeatable jobs before their database rows cascade-delete. */ +export const cancelDatabaseScheduleJobs = async ( + databaseType: DatabaseScheduleType, + databaseId: string, +) => { + const jobs = await db.query.schedules.findMany({ + where: eq(schedules[databaseScheduleIdColumns[databaseType]], databaseId), + columns: { + scheduleId: true, + cronExpression: true, + timezone: true, + }, + }); + + // Include disabled jobs in case an earlier disable failed to unregister one. + for (const job of jobs) { + if (IS_CLOUD) { + const response: unknown = await removeJob({ + type: "schedule", + scheduleId: job.scheduleId, + cronSchedule: job.cronExpression, + timezone: job.timezone, + }); + // The shared HTTP client resolves JSON even for error status codes. + // Require the worker's acknowledgement before cascading away the job. + if ( + typeof response !== "object" || + response === null || + !("result" in response) || + typeof response.result !== "boolean" + ) { + throw new Error( + `Cloud scheduler did not confirm removal of database schedule ${job.scheduleId}`, + ); + } + } else { + removeScheduleJob(job.scheduleId); + } + } +}; + +/** Project deletion also cascades through all of its database schedules. */ +export const cancelProjectDatabaseScheduleJobs = async ( + environments: Awaited>["environments"], +) => { + for (const environment of environments) { + for (const database of environment.postgres) { + await cancelDatabaseScheduleJobs("postgres", database.postgresId); + } + for (const database of environment.mysql) { + await cancelDatabaseScheduleJobs("mysql", database.mysqlId); + } + for (const database of environment.mariadb) { + await cancelDatabaseScheduleJobs("mariadb", database.mariadbId); + } + for (const database of environment.mongo) { + await cancelDatabaseScheduleJobs("mongo", database.mongoId); + } + for (const database of environment.redis) { + await cancelDatabaseScheduleJobs("redis", database.redisId); + } + for (const database of environment.libsql) { + await cancelDatabaseScheduleJobs("libsql", database.libsqlId); + } + } +}; diff --git a/apps/schedules/src/utils.ts b/apps/schedules/src/utils.ts index 85de85f9a98..29d265edcbe 100644 --- a/apps/schedules/src/utils.ts +++ b/apps/schedules/src/utils.ts @@ -193,6 +193,12 @@ export const initializeJobs = async () => { }, }, server: true, + postgres: { with: { server: true } }, + mysql: { with: { server: true } }, + mariadb: { with: { server: true } }, + mongo: { with: { server: true } }, + redis: { with: { server: true } }, + libsql: { with: { server: true } }, }, }); @@ -207,6 +213,25 @@ export const initializeJobs = async () => { if (schedule.compose) { return schedule.compose.server?.serverStatus === "active"; } + if (schedule.postgres) { + return schedule.postgres.server?.serverStatus === "active"; + } + if (schedule.mysql) { + return schedule.mysql.server?.serverStatus === "active"; + } + if (schedule.mariadb) { + return schedule.mariadb.server?.serverStatus === "active"; + } + if (schedule.mongo) { + return schedule.mongo.server?.serverStatus === "active"; + } + if (schedule.redis) { + return schedule.redis.server?.serverStatus === "active"; + } + if (schedule.libsql) { + return schedule.libsql.server?.serverStatus === "active"; + } + return false; }, ); @@ -216,6 +241,7 @@ export const initializeJobs = async () => { scheduleId: schedule.scheduleId, type: "schedule", cronSchedule: schedule.cronExpression, + timezone: schedule.timezone, }); } catch (error) { logger.error(error, `Failed to schedule ${schedule.scheduleId}`); @@ -253,6 +279,7 @@ export const initializeJobs = async () => { if (volumeBackup.compose) { return volumeBackup.compose.server?.serverStatus === "active"; } + return false; }, ); diff --git a/packages/server/src/db/schema/libsql.ts b/packages/server/src/db/schema/libsql.ts index 9658ad93a23..4261f254662 100644 --- a/packages/server/src/db/schema/libsql.ts +++ b/packages/server/src/db/schema/libsql.ts @@ -13,6 +13,7 @@ import { z } from "zod"; import { backups } from "./backups"; import { environments } from "./environment"; import { mounts } from "./mount"; +import { schedules } from "./schedule"; import { server } from "./server"; import { applicationStatus, @@ -102,6 +103,7 @@ export const libsql = pgTable("libsql", { }); export const libsqlRelations = relations(libsql, ({ one, many }) => ({ + schedules: many(schedules), environment: one(environments, { fields: [libsql.environmentId], references: [environments.environmentId], diff --git a/packages/server/src/db/schema/mariadb.ts b/packages/server/src/db/schema/mariadb.ts index 48b48f5b633..9519015e8fd 100644 --- a/packages/server/src/db/schema/mariadb.ts +++ b/packages/server/src/db/schema/mariadb.ts @@ -13,6 +13,7 @@ import { z } from "zod"; import { backups } from "./backups"; import { environments } from "./environment"; import { mounts } from "./mount"; +import { schedules } from "./schedule"; import { server } from "./server"; import { applicationStatus, @@ -102,6 +103,7 @@ export const mariadb = pgTable("mariadb", { }); export const mariadbRelations = relations(mariadb, ({ one, many }) => ({ + schedules: many(schedules), environment: one(environments, { fields: [mariadb.environmentId], references: [environments.environmentId], diff --git a/packages/server/src/db/schema/mongo.ts b/packages/server/src/db/schema/mongo.ts index 5e91a72d128..b01fa9c2201 100644 --- a/packages/server/src/db/schema/mongo.ts +++ b/packages/server/src/db/schema/mongo.ts @@ -13,6 +13,7 @@ import { z } from "zod"; import { backups } from "./backups"; import { environments } from "./environment"; import { mounts } from "./mount"; +import { schedules } from "./schedule"; import { server } from "./server"; import { applicationStatus, @@ -99,6 +100,7 @@ export const mongo = pgTable("mongo", { }); export const mongoRelations = relations(mongo, ({ one, many }) => ({ + schedules: many(schedules), environment: one(environments, { fields: [mongo.environmentId], references: [environments.environmentId], diff --git a/packages/server/src/db/schema/mysql.ts b/packages/server/src/db/schema/mysql.ts index 3a58f5aad2d..e45b1151427 100644 --- a/packages/server/src/db/schema/mysql.ts +++ b/packages/server/src/db/schema/mysql.ts @@ -13,6 +13,7 @@ import { z } from "zod"; import { backups } from "./backups"; import { environments } from "./environment"; import { mounts } from "./mount"; +import { schedules } from "./schedule"; import { server } from "./server"; import { applicationStatus, @@ -100,6 +101,7 @@ export const mysql = pgTable("mysql", { }); export const mysqlRelations = relations(mysql, ({ one, many }) => ({ + schedules: many(schedules), environment: one(environments, { fields: [mysql.environmentId], references: [environments.environmentId], diff --git a/packages/server/src/db/schema/postgres.ts b/packages/server/src/db/schema/postgres.ts index 067b83e43b3..2e5a4f26687 100644 --- a/packages/server/src/db/schema/postgres.ts +++ b/packages/server/src/db/schema/postgres.ts @@ -13,6 +13,7 @@ import { z } from "zod"; import { backups } from "./backups"; import { environments } from "./environment"; import { mounts } from "./mount"; +import { schedules } from "./schedule"; import { server } from "./server"; import { applicationStatus, @@ -100,6 +101,7 @@ export const postgres = pgTable("postgres", { }); export const postgresRelations = relations(postgres, ({ one, many }) => ({ + schedules: many(schedules), environment: one(environments, { fields: [postgres.environmentId], references: [environments.environmentId], diff --git a/packages/server/src/db/schema/redis.ts b/packages/server/src/db/schema/redis.ts index 95ffd8503e8..cbff2ada30b 100644 --- a/packages/server/src/db/schema/redis.ts +++ b/packages/server/src/db/schema/redis.ts @@ -12,6 +12,7 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { environments } from "./environment"; import { mounts } from "./mount"; +import { schedules } from "./schedule"; import { server } from "./server"; import { applicationStatus, @@ -94,6 +95,7 @@ export const redis = pgTable("redis", { }); export const redisRelations = relations(redis, ({ one, many }) => ({ + schedules: many(schedules), environment: one(environments, { fields: [redis.environmentId], references: [environments.environmentId], diff --git a/packages/server/src/db/schema/schedule.ts b/packages/server/src/db/schema/schedule.ts index 55c831f548c..5ecaa8e88d8 100644 --- a/packages/server/src/db/schema/schedule.ts +++ b/packages/server/src/db/schema/schedule.ts @@ -7,15 +7,52 @@ import { organization } from "./account"; import { applications } from "./application"; import { compose } from "./compose"; import { deployments } from "./deployment"; +import { libsql } from "./libsql"; +import { mariadb } from "./mariadb"; +import { mongo } from "./mongo"; +import { mysql } from "./mysql"; +import { postgres } from "./postgres"; +import { redis } from "./redis"; import { server } from "./server"; import { generateAppName } from "./utils"; export const shellTypes = pgEnum("shellType", ["bash", "sh"]); +export const databaseScheduleTypes = [ + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", +] as const; + +export type DatabaseScheduleType = (typeof databaseScheduleTypes)[number]; + +export const isDatabaseScheduleType = ( + type: string | null | undefined, +): type is DatabaseScheduleType => + databaseScheduleTypes.some((value) => value === type); + +export const databaseScheduleIdColumns = { + postgres: "postgresId", + mysql: "mysqlId", + mariadb: "mariadbId", + mongo: "mongoId", + redis: "redisId", + libsql: "libsqlId", +} as const; + export const scheduleType = pgEnum("scheduleType", [ "application", "compose", "server", "dokploy-server", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", ]); export const schedules = pgTable("schedule", { @@ -43,6 +80,25 @@ export const schedules = pgTable("schedule", { composeId: text("composeId").references(() => compose.composeId, { onDelete: "cascade", }), + postgresId: text("postgresId").references(() => postgres.postgresId, { + onDelete: "cascade", + }), + mysqlId: text("mysqlId").references(() => mysql.mysqlId, { + onDelete: "cascade", + }), + mariadbId: text("mariadbId").references(() => mariadb.mariadbId, { + onDelete: "cascade", + }), + mongoId: text("mongoId").references(() => mongo.mongoId, { + onDelete: "cascade", + }), + redisId: text("redisId").references(() => redis.redisId, { + onDelete: "cascade", + }), + libsqlId: text("libsqlId").references(() => libsql.libsqlId, { + onDelete: "cascade", + }), + serverId: text("serverId").references(() => server.serverId, { onDelete: "cascade", }), @@ -59,6 +115,30 @@ export const schedules = pgTable("schedule", { export type Schedule = typeof schedules.$inferSelect; export type ScheduleType = Schedule["scheduleType"]; +export const getDatabaseScheduleId = (schedule: Partial) => + isDatabaseScheduleType(schedule.scheduleType) + ? schedule[databaseScheduleIdColumns[schedule.scheduleType]] + : null; + +// Prevent schedules from being bound to multiple services or to a database +// other than the one identified by scheduleType. +export const isValidDatabaseScheduleTarget = (input: Partial) => { + const linkedTargets = Object.values(databaseScheduleIdColumns).filter( + (column) => Boolean(input[column]), + ); + if (!isDatabaseScheduleType(input.scheduleType)) { + return linkedTargets.length === 0; + } + return ( + linkedTargets.length === 1 && + Boolean(input[databaseScheduleIdColumns[input.scheduleType]]) && + !input.applicationId && + !input.composeId && + !input.serverId && + !input.organizationId + ); +}; + export const schedulesRelations = relations(schedules, ({ one, many }) => ({ application: one(applications, { fields: [schedules.applicationId], @@ -68,6 +148,31 @@ export const schedulesRelations = relations(schedules, ({ one, many }) => ({ fields: [schedules.composeId], references: [compose.composeId], }), + postgres: one(postgres, { + fields: [schedules.postgresId], + references: [postgres.postgresId], + }), + mysql: one(mysql, { + fields: [schedules.mysqlId], + references: [mysql.mysqlId], + }), + mariadb: one(mariadb, { + fields: [schedules.mariadbId], + references: [mariadb.mariadbId], + }), + mongo: one(mongo, { + fields: [schedules.mongoId], + references: [mongo.mongoId], + }), + redis: one(redis, { + fields: [schedules.redisId], + references: [redis.redisId], + }), + libsql: one(libsql, { + fields: [schedules.libsqlId], + references: [libsql.libsqlId], + }), + server: one(server, { fields: [schedules.serverId], references: [server.serverId], @@ -80,7 +185,8 @@ export const schedulesRelations = relations(schedules, ({ one, many }) => ({ })); export const createScheduleSchema = createInsertSchema(schedules, { - scheduleType: z.enum(["application", "compose", "server", "dokploy-server"]), + scheduleType: z.enum(scheduleType.enumValues), + shellType: z.enum(shellTypes.enumValues), }); export const updateScheduleSchema = createScheduleSchema.extend({ diff --git a/packages/server/src/services/deployment.ts b/packages/server/src/services/deployment.ts index d7e1a43c418..e153b4f4791 100644 --- a/packages/server/src/services/deployment.ts +++ b/packages/server/src/services/deployment.ts @@ -38,7 +38,7 @@ import { updatePreviewDeployment, } from "./preview-deployment"; import { removeRollbackById } from "./rollbacks"; -import { findScheduleById } from "./schedule"; +import { findScheduleById, getScheduleServerId } from "./schedule"; import { findServerById, type Server } from "./server"; import { findVolumeBackupById } from "./volume-backups"; @@ -449,10 +449,7 @@ export const createDeploymentSchedule = async ( ) => { const schedule = await findScheduleById(deployment.scheduleId); - const serverId = - schedule.application?.serverId || - schedule.compose?.serverId || - schedule.server?.serverId; + const serverId = getScheduleServerId(schedule); await removeLastTenDeployments(deployment.scheduleId, "schedule", serverId); await db .update(deployments) @@ -630,8 +627,13 @@ export const removeDeployment = async (deploymentId: string) => { const logPath = path.join(deployment.logPath); if (logPath && logPath !== ".") { const command = `rm -f ${logPath};`; - if (deployment.serverId) { - await execAsyncRemote(deployment.serverId, command); + const serverId = + deployment.serverId || + (deployment.scheduleId + ? getScheduleServerId(await findScheduleById(deployment.scheduleId)) + : null); + if (serverId) { + await execAsyncRemote(serverId, command); } else { await execAsync(command); } diff --git a/packages/server/src/services/schedule.ts b/packages/server/src/services/schedule.ts index 59b75cc0da4..6c51aac97a8 100644 --- a/packages/server/src/services/schedule.ts +++ b/packages/server/src/services/schedule.ts @@ -8,7 +8,11 @@ import type { createScheduleSchema, updateScheduleSchema, } from "../db/schema/schedule"; -import { type Schedule, schedules } from "../db/schema/schedule"; +import { + isDatabaseScheduleType, + type Schedule, + schedules, +} from "../db/schema/schedule"; import { encodeBase64 } from "../utils/docker/utils"; import { execAsync, execAsyncRemote } from "../utils/process/execAsync"; import { findMemberByUserId } from "./permission"; @@ -16,6 +20,25 @@ import { findServerById } from "./server"; export type ScheduleExtended = Awaited>; +// Only the container identity and ownership chain are needed to manage +// database schedules. In particular, never load database credentials or the +// encrypted environment values from the database, environment, or project. +const databaseScheduleRelation = { + columns: { + name: true, + appName: true, + serverId: true, + }, + with: { + environment: { + columns: { environmentId: true }, + with: { + project: { columns: { organizationId: true } }, + }, + }, + }, +} as const; + // Host-level schedules (server / dokploy-server) run their script as root on the // host and must stay restricted to owners/admins, regardless of whether the // request is also tied to a service. Attaching an accessible applicationId must @@ -62,7 +85,13 @@ export const createSchedule = async ( const { scheduleId, ...rest } = input; const [newSchedule] = await db .insert(schedules) - .values(rest as typeof schedules.$inferInsert) + .values({ + ...rest, + // Database images commonly provide /bin/sh but not bash. The UI uses + // sh by default; keep API-created database schedules consistent. + ...(isDatabaseScheduleType(rest.scheduleType) && + rest.shellType === undefined && { shellType: "sh" as const }), + } as typeof schedules.$inferInsert) .returning(); if ( @@ -103,6 +132,13 @@ export const findScheduleById = async (scheduleId: string) => { }, }, }, + postgres: databaseScheduleRelation, + mysql: databaseScheduleRelation, + mariadb: databaseScheduleRelation, + mongo: databaseScheduleRelation, + redis: databaseScheduleRelation, + libsql: databaseScheduleRelation, + server: { with: { organization: true, @@ -129,6 +165,16 @@ export const findScheduleOrganizationId = async (scheduleId: string) => { if (schedule?.compose) { return schedule?.compose?.environment?.project?.organizationId; } + for (const database of [ + schedule.postgres, + schedule.mysql, + schedule.mariadb, + schedule.mongo, + schedule.redis, + schedule.libsql, + ]) { + if (database) return database.environment.project.organizationId; + } if (schedule?.server) { return schedule?.server?.organization?.id; } @@ -138,12 +184,21 @@ export const findScheduleOrganizationId = async (scheduleId: string) => { return null; }; +export const getScheduleServerId = (schedule: ScheduleExtended) => + schedule.serverId || + schedule.application?.serverId || + schedule.compose?.serverId || + schedule.postgres?.serverId || + schedule.mysql?.serverId || + schedule.mariadb?.serverId || + schedule.mongo?.serverId || + schedule.redis?.serverId || + schedule.libsql?.serverId || + null; + export const deleteSchedule = async (scheduleId: string) => { const schedule = await findScheduleById(scheduleId); - const serverId = - schedule?.serverId || - schedule?.application?.serverId || - schedule?.compose?.serverId; + const serverId = getScheduleServerId(schedule); const { SCHEDULES_PATH } = paths(!!serverId); const fullPath = path.join(SCHEDULES_PATH, schedule?.appName || ""); diff --git a/packages/server/src/utils/process/remoteStream.ts b/packages/server/src/utils/process/remoteStream.ts index c82be4e6da1..9118a810129 100644 --- a/packages/server/src/utils/process/remoteStream.ts +++ b/packages/server/src/utils/process/remoteStream.ts @@ -107,22 +107,37 @@ export const pipeBetweenServers = async ({ onProgress?.(bytes); }); from.stdout.on("error", () => {}); - from.stdout.pipe(to.stdin); - const targetExit = to.exit.then((code) => { - if (code !== 0) from.close(); - return code; - }); + let sourceStopped = false; + const stopSource = () => { + if (sourceStopped) return; + sourceStopped = true; + from.stdout.unpipe(to.stdin); + from.stdout.resume(); + from.close(); + }; + + if (to.stdin.writable) { + from.stdout.pipe(to.stdin); + } else { + stopSource(); + } + + const targetExit = to.exit + .then((code) => { + if (code !== 0) { + throw new Error(`target exited with code ${code}: ${targetError()}`); + } + return code; + }) + .catch((error) => { + stopSource(); + throw error; + }); - const [sourceCode, targetCode] = await Promise.all([from.exit, targetExit]); - const failures = [ - sourceCode !== 0 && - `source exited with code ${sourceCode}: ${sourceError()}`, - targetCode !== 0 && - `target exited with code ${targetCode}: ${targetError()}`, - ].filter(Boolean); - if (failures.length > 0) { - throw new Error(failures.join("\n")); + const [sourceCode] = await Promise.all([from.exit, targetExit]); + if (sourceCode !== 0) { + throw new Error(`source exited with code ${sourceCode}: ${sourceError()}`); } return bytes; }; diff --git a/packages/server/src/utils/schedules/utils.ts b/packages/server/src/utils/schedules/utils.ts index 65d01afed30..716dcd31b86 100644 --- a/packages/server/src/utils/schedules/utils.ts +++ b/packages/server/src/utils/schedules/utils.ts @@ -1,7 +1,10 @@ import { createWriteStream } from "node:fs"; import path from "node:path"; import { IS_CLOUD, paths } from "@dokploy/server/constants"; -import type { Schedule } from "@dokploy/server/db/schema/schedule"; +import { + isDatabaseScheduleType, + type Schedule, +} from "@dokploy/server/db/schema/schedule"; import { createDeploymentSchedule, updateDeployment, @@ -44,6 +47,12 @@ export const runCommand = async (scheduleId: string) => { shellType, scheduleType, compose, + postgres, + mysql, + mariadb, + mongo, + redis, + libsql, serviceName, appName, serverId, @@ -56,7 +65,11 @@ export const runCommand = async (scheduleId: string) => { }); try { - if (scheduleType === "application" || scheduleType === "compose") { + if ( + scheduleType === "application" || + scheduleType === "compose" || + isDatabaseScheduleType(scheduleType) + ) { let containerId = ""; let serverId = ""; if (scheduleType === "application" && application) { @@ -73,9 +86,22 @@ export const runCommand = async (scheduleId: string) => { serverId = compose.serverId || ""; } + const database = isDatabaseScheduleType(scheduleType) + ? { postgres, mysql, mariadb, mongo, redis, libsql }[scheduleType] + : null; + if (database) { + const container = await getServiceContainer( + database.appName, + database.serverId, + ); + containerId = container?.Id || ""; + serverId = database.serverId || ""; + } + if (!containerId) { - const target = - scheduleType === "compose" + const target = database + ? `${scheduleType} database '${database.name}'` + : scheduleType === "compose" ? `service '${serviceName}' of compose '${compose?.name}'` : `application '${application?.appName}'`; const message = `Container not found for ${target}, make sure the service is running`;