import { db } from "@dokploy/server/db";import { type apiCreateEnvironment, type apiDuplicateEnvironment, environments,} from "@dokploy/server/db/schema";import { TRPCError } from "@trpc/server";import { asc, eq } from "drizzle-orm";import type { z } from "zod"; export type Environment = typeof environments.$inferSelect; export const createEnvironment = async ( input: z.infer<typeof apiCreateEnvironment>,) => { const newEnvironment = await db .insert(environments) .values({ ...input, }) .returning() .then((value) => value[0]); if (!newEnvironment) { throw new TRPCError({ code: "BAD_REQUEST", message: "Error creating the environment", }); } return newEnvironment;}; export const findEnvironmentById = async (environmentId: string) => { const environment = await db.query.environments.findFirst({ where: eq(environments.environmentId, environmentId), columns: { name: true, description: true, environmentId: true, isDefault: true, projectId: true, env: true, }, with: { applications: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { name: true, applicationId: true, createdAt: true, applicationStatus: true, description: true, serverId: true, icon: true, }, }, mariadb: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { mariadbId: true, name: true, createdAt: true, applicationStatus: true, description: true, serverId: true, }, }, mongo: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { mongoId: true, name: true, createdAt: true, applicationStatus: true, description: true, serverId: true, }, }, mysql: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { mysqlId: true, name: true, createdAt: true, applicationStatus: true, description: true, serverId: true, }, }, postgres: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { postgresId: true, name: true, description: true, createdAt: true, applicationStatus: true, serverId: true, }, }, redis: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { redisId: true, name: true, createdAt: true, applicationStatus: true, description: true, serverId: true, }, }, compose: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { composeId: true, name: true, createdAt: true, composeStatus: true, description: true, serverId: true, }, }, libsql: { with: { server: { columns: { name: true, serverId: true, }, }, }, columns: { libsqlId: true, name: true, createdAt: true, applicationStatus: true, description: true, serverId: true, }, }, project: true, }, }); if (!environment) { throw new TRPCError({ code: "NOT_FOUND", message: "Environment not found", }); } return environment;}; export const findEnvironmentsByProjectId = async (projectId: string) => { const projectEnvironments = await db.query.environments.findMany({ where: eq(environments.projectId, projectId), orderBy: asc(environments.createdAt), with: { applications: true, mariadb: true, mongo: true, mysql: true, postgres: true, redis: true, compose: true, libsql: true, project: true, }, columns: { name: true, description: true, environmentId: true, isDefault: true, }, }); return projectEnvironments;}; const environmentHasServices = ( env: Awaited<ReturnType<typeof findEnvironmentById>>,) => { return ( (env.applications?.length ?? 0) > 0 || (env.compose?.length ?? 0) > 0 || (env.libsql?.length ?? 0) > 0 || (env.mariadb?.length ?? 0) > 0 || (env.mongo?.length ?? 0) > 0 || (env.mysql?.length ?? 0) > 0 || (env.postgres?.length ?? 0) > 0 || (env.redis?.length ?? 0) > 0 );}; export const deleteEnvironment = async (environmentId: string) => { const currentEnvironment = await findEnvironmentById(environmentId); if (currentEnvironment.isDefault) { throw new TRPCError({ code: "BAD_REQUEST", message: "You cannot delete the default environment", }); } if (environmentHasServices(currentEnvironment)) { throw new TRPCError({ code: "BAD_REQUEST", message: "Cannot delete environment: it has active services. Delete all services first.", }); } const deletedEnvironment = await db .delete(environments) .where(eq(environments.environmentId, environmentId)) .returning() .then((value) => value[0]); return deletedEnvironment;}; export const updateEnvironmentById = async ( environmentId: string, environmentData: Partial<Environment>,) => { const result = await db .update(environments) .set({ ...environmentData, }) .where(eq(environments.environmentId, environmentId)) .returning() .then((res) => res[0]); return result;}; export const duplicateEnvironment = async ( input: z.infer<typeof apiDuplicateEnvironment>,) => { // Find the original environment const originalEnvironment = await findEnvironmentById(input.environmentId); // Create a new environment with the provided name and description const newEnvironment = await db .insert(environments) .values({ name: input.name, description: input.description || originalEnvironment.description, projectId: originalEnvironment.projectId, env: originalEnvironment.env, }) .returning() .then((value) => value[0]); if (!newEnvironment) { throw new TRPCError({ code: "BAD_REQUEST", message: "Error duplicating the environment", }); } return newEnvironment;}; interface EnvironmentWithServices { applications: { applicationId: string }[]; compose: { composeId: string }[]; libsql: { libsqlId: string }[]; mariadb: { mariadbId: string }[]; mongo: { mongoId: string }[]; mysql: { mysqlId: string }[]; postgres: { postgresId: string }[]; redis: { redisId: string }[];} export const filterEnvironmentServices = <T extends EnvironmentWithServices>( environment: T, accessedServices: string[],): T => ({ ...environment, applications: environment.applications.filter((app) => accessedServices.includes(app.applicationId), ), compose: environment.compose.filter((comp) => accessedServices.includes(comp.composeId), ), libsql: environment.libsql.filter((db) => accessedServices.includes(db.libsqlId), ), mariadb: environment.mariadb.filter((db) => accessedServices.includes(db.mariadbId), ), mongo: environment.mongo.filter((db) => accessedServices.includes(db.mongoId), ), mysql: environment.mysql.filter((db) => accessedServices.includes(db.mysqlId), ), postgres: environment.postgres.filter((db) => accessedServices.includes(db.postgresId), ), redis: environment.redis.filter((db) => accessedServices.includes(db.redisId), ),}); export const createProductionEnvironment = async (projectId: string) => { const newEnvironment = await db .insert(environments) .values({ name: "production", description: "Production environment", projectId, isDefault: true, }) .returning() .then((value) => value[0]); if (!newEnvironment) { throw new TRPCError({ code: "BAD_REQUEST", message: "Error creating the production environment", }); } return newEnvironment;};
import { db } from "@dokploy/server/db";
import {
type apiCreateEnvironment,
type apiDuplicateEnvironment,
environments,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import { asc, eq } from "drizzle-orm";
import type { z } from "zod";
export type Environment = typeof environments.$inferSelect;
export const createEnvironment = async (
input: z.infer<typeof apiCreateEnvironment>,
) => {
const newEnvironment = await db
.insert(environments)
.values({
...input,
})
.returning()
.then((value) => value[0]);
if (!newEnvironment) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the environment",
});
}
return newEnvironment;
};
export const findEnvironmentById = async (environmentId: string) => {
const environment = await db.query.environments.findFirst({
where: eq(environments.environmentId, environmentId),
columns: {
name: true,
description: true,
environmentId: true,
isDefault: true,
projectId: true,
env: true,
},
with: {
applications: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
name: true,
applicationId: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
icon: true,
},
},
mariadb: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
mariadbId: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
mongo: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
mongoId: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
mysql: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
mysqlId: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
postgres: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
postgresId: true,
name: true,
description: true,
createdAt: true,
applicationStatus: true,
serverId: true,
},
},
redis: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
redisId: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
compose: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
composeId: true,
name: true,
createdAt: true,
composeStatus: true,
description: true,
serverId: true,
},
},
libsql: {
with: {
server: {
columns: {
name: true,
serverId: true,
},
},
},
columns: {
libsqlId: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
project: true,
},
});
if (!environment) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Environment not found",
});
}
return environment;
};
export const findEnvironmentsByProjectId = async (projectId: string) => {
const projectEnvironments = await db.query.environments.findMany({
where: eq(environments.projectId, projectId),
orderBy: asc(environments.createdAt),
with: {
applications: true,
mariadb: true,
mongo: true,
mysql: true,
postgres: true,
redis: true,
compose: true,
libsql: true,
project: true,
},
columns: {
name: true,
description: true,
environmentId: true,
isDefault: true,
},
});
return projectEnvironments;
};
const environmentHasServices = (
env: Awaited<ReturnType<typeof findEnvironmentById>>,
) => {
return (
(env.applications?.length ?? 0) > 0 ||
(env.compose?.length ?? 0) > 0 ||
(env.libsql?.length ?? 0) > 0 ||
(env.mariadb?.length ?? 0) > 0 ||
(env.mongo?.length ?? 0) > 0 ||
(env.mysql?.length ?? 0) > 0 ||
(env.postgres?.length ?? 0) > 0 ||
(env.redis?.length ?? 0) > 0
);
};
export const deleteEnvironment = async (environmentId: string) => {
const currentEnvironment = await findEnvironmentById(environmentId);
if (currentEnvironment.isDefault) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "You cannot delete the default environment",
});
}
if (environmentHasServices(currentEnvironment)) {
throw new TRPCError({
code: "BAD_REQUEST",
message:
"Cannot delete environment: it has active services. Delete all services first.",
});
}
const deletedEnvironment = await db
.delete(environments)
.where(eq(environments.environmentId, environmentId))
.returning()
.then((value) => value[0]);
return deletedEnvironment;
};
export const updateEnvironmentById = async (
environmentId: string,
environmentData: Partial<Environment>,
) => {
const result = await db
.update(environments)
.set({
...environmentData,
})
.where(eq(environments.environmentId, environmentId))
.returning()
.then((res) => res[0]);
return result;
};
export const duplicateEnvironment = async (
input: z.infer<typeof apiDuplicateEnvironment>,
) => {
// Find the original environment
const originalEnvironment = await findEnvironmentById(input.environmentId);
// Create a new environment with the provided name and description
const newEnvironment = await db
.insert(environments)
.values({
name: input.name,
description: input.description || originalEnvironment.description,
projectId: originalEnvironment.projectId,
env: originalEnvironment.env,
})
.returning()
.then((value) => value[0]);
if (!newEnvironment) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error duplicating the environment",
});
}
return newEnvironment;
};
interface EnvironmentWithServices {
applications: { applicationId: string }[];
compose: { composeId: string }[];
libsql: { libsqlId: string }[];
mariadb: { mariadbId: string }[];
mongo: { mongoId: string }[];
mysql: { mysqlId: string }[];
postgres: { postgresId: string }[];
redis: { redisId: string }[];
}
export const filterEnvironmentServices = <T extends EnvironmentWithServices>(
environment: T,
accessedServices: string[],
): T => ({
...environment,
applications: environment.applications.filter((app) =>
accessedServices.includes(app.applicationId),
),
compose: environment.compose.filter((comp) =>
accessedServices.includes(comp.composeId),
),
libsql: environment.libsql.filter((db) =>
accessedServices.includes(db.libsqlId),
),
mariadb: environment.mariadb.filter((db) =>
accessedServices.includes(db.mariadbId),
),
mongo: environment.mongo.filter((db) =>
accessedServices.includes(db.mongoId),
),
mysql: environment.mysql.filter((db) =>
accessedServices.includes(db.mysqlId),
),
postgres: environment.postgres.filter((db) =>
accessedServices.includes(db.postgresId),
),
redis: environment.redis.filter((db) =>
accessedServices.includes(db.redisId),
),
});
export const createProductionEnvironment = async (projectId: string) => {
const newEnvironment = await db
.insert(environments)
.values({
name: "production",
description: "Production environment",
projectId,
isDefault: true,
})
.returning()
.then((value) => value[0]);
if (!newEnvironment) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the production environment",
});
}
return newEnvironment;
};